fixed -stickyWindows option
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int x, y, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo = {0, 0, 0};\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 \r
205 /* [AS] Support for background textures */\r
206 #define BACK_TEXTURE_MODE_DISABLED      0\r
207 #define BACK_TEXTURE_MODE_PLAIN         1\r
208 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #define oldDialog (_winmajor < 4)\r
221 #endif\r
222 \r
223 char *defaultTextAttribs[] = \r
224 {\r
225   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
226   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
227   COLOR_NONE\r
228 };\r
229 \r
230 typedef struct {\r
231   char *name;\r
232   int squareSize;\r
233   int lineGap;\r
234   int smallLayout;\r
235   int tinyLayout;\r
236   int cliWidth, cliHeight;\r
237 } SizeInfo;\r
238 \r
239 SizeInfo sizeInfo[] = \r
240 {\r
241   { "tiny",     21, 0, 1, 1, 0, 0 },\r
242   { "teeny",    25, 1, 1, 1, 0, 0 },\r
243   { "dinky",    29, 1, 1, 1, 0, 0 },\r
244   { "petite",   33, 1, 1, 1, 0, 0 },\r
245   { "slim",     37, 2, 1, 0, 0, 0 },\r
246   { "small",    40, 2, 1, 0, 0, 0 },\r
247   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
248   { "middling", 49, 2, 0, 0, 0, 0 },\r
249   { "average",  54, 2, 0, 0, 0, 0 },\r
250   { "moderate", 58, 3, 0, 0, 0, 0 },\r
251   { "medium",   64, 3, 0, 0, 0, 0 },\r
252   { "bulky",    72, 3, 0, 0, 0, 0 },\r
253   { "large",    80, 3, 0, 0, 0, 0 },\r
254   { "big",      87, 3, 0, 0, 0, 0 },\r
255   { "huge",     95, 3, 0, 0, 0, 0 },\r
256   { "giant",    108, 3, 0, 0, 0, 0 },\r
257   { "colossal", 116, 4, 0, 0, 0, 0 },\r
258   { "titanic",  129, 4, 0, 0, 0, 0 },\r
259   { NULL, 0, 0, 0, 0, 0, 0 }\r
260 };\r
261 \r
262 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
263 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
264 {\r
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283 };\r
284 \r
285 MyFont *font[NUM_SIZES][NUM_FONTS];\r
286 \r
287 typedef struct {\r
288   char *label;\r
289   int id;\r
290   HWND hwnd;\r
291   WNDPROC wndproc;\r
292 } MyButtonDesc;\r
293 \r
294 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
295 #define N_BUTTONS 5\r
296 \r
297 MyButtonDesc buttonDesc[N_BUTTONS] =\r
298 {\r
299   {"<<", IDM_ToStart, NULL, NULL},\r
300   {"<", IDM_Backward, NULL, NULL},\r
301   {"P", IDM_Pause, NULL, NULL},\r
302   {">", IDM_Forward, NULL, NULL},\r
303   {">>", IDM_ToEnd, NULL, NULL},\r
304 };\r
305 \r
306 int tinyLayout = 0, smallLayout = 0;\r
307 #define MENU_BAR_ITEMS 6\r
308 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
309   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
310   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
311 };\r
312 \r
313 \r
314 MySound sounds[(int)NSoundClasses];\r
315 MyTextAttribs textAttribs[(int)NColorClasses];\r
316 \r
317 MyColorizeAttribs colorizeAttribs[] = {\r
318   { (COLORREF)0, 0, "Shout Text" },\r
319   { (COLORREF)0, 0, "SShout/CShout" },\r
320   { (COLORREF)0, 0, "Channel 1 Text" },\r
321   { (COLORREF)0, 0, "Channel Text" },\r
322   { (COLORREF)0, 0, "Kibitz Text" },\r
323   { (COLORREF)0, 0, "Tell Text" },\r
324   { (COLORREF)0, 0, "Challenge Text" },\r
325   { (COLORREF)0, 0, "Request Text" },\r
326   { (COLORREF)0, 0, "Seek Text" },\r
327   { (COLORREF)0, 0, "Normal Text" },\r
328   { (COLORREF)0, 0, "None" }\r
329 };\r
330 \r
331 \r
332 \r
333 static char *commentTitle;\r
334 static char *commentText;\r
335 static int commentIndex;\r
336 static Boolean editComment = FALSE;\r
337 HWND commentDialog = NULL;\r
338 BOOLEAN commentDialogUp = FALSE;\r
339 static int commentX, commentY, commentH, commentW;\r
340 \r
341 static char *analysisTitle;\r
342 static char *analysisText;\r
343 HWND analysisDialog = NULL;\r
344 BOOLEAN analysisDialogUp = FALSE;\r
345 static int analysisX, analysisY, analysisH, analysisW;\r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 HWND moveHistoryDialog = NULL;\r
416 BOOLEAN moveHistoryDialogUp = FALSE;\r
417 \r
418 WindowPlacement wpMoveHistory;\r
419 \r
420 HWND evalGraphDialog = NULL;\r
421 BOOLEAN evalGraphDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpEvalGraph;\r
424 \r
425 HWND engineOutputDialog = NULL;\r
426 BOOLEAN engineOutputDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEngineOutput;\r
429 \r
430 VOID MoveHistoryPopUp();\r
431 VOID MoveHistoryPopDown();\r
432 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
433 BOOL MoveHistoryIsUp();\r
434 \r
435 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
436 VOID EvalGraphPopUp();\r
437 VOID EvalGraphPopDown();\r
438 BOOL EvalGraphIsUp();\r
439 \r
440 VOID EngineOutputPopUp();\r
441 VOID EngineOutputPopDown();\r
442 BOOL EngineOutputIsUp();\r
443 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
444 \r
445 VOID GothicPopUp(char *title, VariantClass variant);\r
446 /*\r
447  * Setting "frozen" should disable all user input other than deleting\r
448  * the window.  We do this while engines are initializing themselves.\r
449  */\r
450 static int frozen = 0;\r
451 static int oldMenuItemState[MENU_BAR_ITEMS];\r
452 void FreezeUI()\r
453 {\r
454   HMENU hmenu;\r
455   int i;\r
456 \r
457   if (frozen) return;\r
458   frozen = 1;\r
459   hmenu = GetMenu(hwndMain);\r
460   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
461     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
462   }\r
463   DrawMenuBar(hwndMain);\r
464 }\r
465 \r
466 /* Undo a FreezeUI */\r
467 void ThawUI()\r
468 {\r
469   HMENU hmenu;\r
470   int i;\r
471 \r
472   if (!frozen) return;\r
473   frozen = 0;\r
474   hmenu = GetMenu(hwndMain);\r
475   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
476     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
477   }\r
478   DrawMenuBar(hwndMain);\r
479 }\r
480 \r
481 /*---------------------------------------------------------------------------*\\r
482  *\r
483  * WinMain\r
484  *\r
485 \*---------------------------------------------------------------------------*/\r
486 \r
487 int APIENTRY\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
489         LPSTR lpCmdLine, int nCmdShow)\r
490 {\r
491   MSG msg;\r
492   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
493 //  INITCOMMONCONTROLSEX ex;\r
494 \r
495   debugFP = stderr;\r
496 \r
497   LoadLibrary("RICHED32.DLL");\r
498   consoleCF.cbSize = sizeof(CHARFORMAT);\r
499 \r
500   if (!InitApplication(hInstance)) {\r
501     return (FALSE);\r
502   }\r
503   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
504     return (FALSE);\r
505   }\r
506 \r
507 //  InitCommonControlsEx(&ex);\r
508   InitCommonControls();\r
509 \r
510   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
511   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
512   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
513 \r
514   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
515 \r
516   while (GetMessage(&msg, /* message structure */\r
517                     NULL, /* handle of window receiving the message */\r
518                     0,    /* lowest message to examine */\r
519                     0))   /* highest message to examine */\r
520     {\r
521       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
522           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
523           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
524           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
525           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
526           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
527           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
528           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
529           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
531         TranslateMessage(&msg); /* Translates virtual key codes */\r
532         DispatchMessage(&msg);  /* Dispatches message to window */\r
533       }\r
534     }\r
535 \r
536 \r
537   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
538 }\r
539 \r
540 /*---------------------------------------------------------------------------*\\r
541  *\r
542  * Initialization functions\r
543  *\r
544 \*---------------------------------------------------------------------------*/\r
545 \r
546 BOOL\r
547 InitApplication(HINSTANCE hInstance)\r
548 {\r
549   WNDCLASS wc;\r
550 \r
551   /* Fill in window class structure with parameters that describe the */\r
552   /* main window. */\r
553 \r
554   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
555   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
556   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
557   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
558   wc.hInstance     = hInstance;         /* Owner of this class */\r
559   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
560   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
561   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
562   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
563   wc.lpszClassName = szAppName;                 /* Name to register as */\r
564 \r
565   /* Register the window class and return success/failure code. */\r
566   if (!RegisterClass(&wc)) return FALSE;\r
567 \r
568   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
569   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
570   wc.cbClsExtra    = 0;\r
571   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
572   wc.hInstance     = hInstance;\r
573   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
574   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
575   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
576   wc.lpszMenuName  = NULL;\r
577   wc.lpszClassName = szConsoleName;\r
578 \r
579   if (!RegisterClass(&wc)) return FALSE;\r
580   return TRUE;\r
581 }\r
582 \r
583 \r
584 /* Set by InitInstance, used by EnsureOnScreen */\r
585 int screenHeight, screenWidth;\r
586 \r
587 void\r
588 EnsureOnScreen(int *x, int *y)\r
589 {\r
590 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
591   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
592   if (*x > screenWidth - 32) *x = 0;\r
593   if (*y > screenHeight - 32) *y = 0;\r
594   if (*x < 0) *x = 0;\r
595   if (*y < 0) *y = 0;\r
596 //  if (*x < 10) *x = 10;\r
597 //  if (*y < gap) *y = gap;\r
598 }\r
599 \r
600 BOOL\r
601 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
602 {\r
603   HWND hwnd; /* Main window handle. */\r
604   int ibs;\r
605   WINDOWPLACEMENT wp;\r
606   char *filepart;\r
607 \r
608   hInst = hInstance;    /* Store instance handle in our global variable */\r
609 \r
610   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
611     *filepart = NULLCHAR;\r
612   } else {\r
613     GetCurrentDirectory(MSG_SIZ, installDir);\r
614   }\r
615   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
616   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
617   if (appData.debugMode) {\r
618     debugFP = fopen(appData.nameOfDebugFile, "w");\r
619     setbuf(debugFP, NULL);\r
620   }\r
621 \r
622   InitBackEnd1();\r
623 \r
624 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
625 //  InitEngineUCI( installDir, &second );\r
626 \r
627   /* Create a main window for this application instance. */\r
628   hwnd = CreateWindow(szAppName, szTitle,\r
629                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
630                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
631                       NULL, NULL, hInstance, NULL);\r
632   hwndMain = hwnd;\r
633 \r
634   /* If window could not be created, return "failure" */\r
635   if (!hwnd) {\r
636     return (FALSE);\r
637   }\r
638 \r
639   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
640   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
641       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642 \r
643       if (first.programLogo == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
645       }\r
646   } else if(appData.autoLogo) {\r
647       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
648         char buf[MSG_SIZ];\r
649         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
650         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
651       }\r
652   }\r
653 \r
654   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
655       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
656 \r
657       if (second.programLogo == NULL && appData.debugMode) {\r
658           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
659       }\r
660   } else if(appData.autoLogo) {\r
661       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
662         char buf[MSG_SIZ];\r
663         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
664         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
665       }\r
666   }\r
667 \r
668   iconWhite = LoadIcon(hInstance, "icon_white");\r
669   iconBlack = LoadIcon(hInstance, "icon_black");\r
670   iconCurrent = iconWhite;\r
671   InitDrawingColors();\r
672   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
673   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
674   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
675     /* Compute window size for each board size, and use the largest\r
676        size that fits on this screen as the default. */\r
677     InitDrawingSizes((BoardSize)ibs, 0);\r
678     if (boardSize == (BoardSize)-1 &&\r
679         winHeight <= screenHeight\r
680            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
681         && winWidth <= screenWidth) {\r
682       boardSize = (BoardSize)ibs;\r
683     }\r
684   }\r
685 \r
686   InitDrawingSizes(boardSize, 0);\r
687   InitMenuChecks();\r
688   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
689 \r
690   /* [AS] Load textures if specified */\r
691   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
692   \r
693   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
694       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       liteBackTextureMode = appData.liteBackTextureMode;\r
696 \r
697       if (liteBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
699       }\r
700   }\r
701   \r
702   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
703       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
704       darkBackTextureMode = appData.darkBackTextureMode;\r
705 \r
706       if (darkBackTexture == NULL && appData.debugMode) {\r
707           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
708       }\r
709   }\r
710 \r
711   mysrandom( (unsigned) time(NULL) );\r
712 \r
713   /* [AS] Restore layout */\r
714   if( wpMoveHistory.visible ) {\r
715       MoveHistoryPopUp();\r
716   }\r
717 \r
718   if( wpEvalGraph.visible ) {\r
719       EvalGraphPopUp();\r
720   }\r
721 \r
722   if( wpEngineOutput.visible ) {\r
723       EngineOutputPopUp();\r
724   }\r
725 \r
726   InitBackEnd2();\r
727 \r
728   /* Make the window visible; update its client area; and return "success" */\r
729   EnsureOnScreen(&boardX, &boardY);\r
730   wp.length = sizeof(WINDOWPLACEMENT);\r
731   wp.flags = 0;\r
732   wp.showCmd = nCmdShow;\r
733   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
734   wp.rcNormalPosition.left = boardX;\r
735   wp.rcNormalPosition.right = boardX + winWidth;\r
736   wp.rcNormalPosition.top = boardY;\r
737   wp.rcNormalPosition.bottom = boardY + winHeight;\r
738   SetWindowPlacement(hwndMain, &wp);\r
739 \r
740   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
741                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
742 \r
743 #if 0\r
744   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
745   if( gameInfo.variant != VariantFischeRandom ) {\r
746       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
747   }\r
748 #endif\r
749   if (hwndConsole) {\r
750 #if AOT_CONSOLE\r
751     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
752                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
753 #endif\r
754     ShowWindow(hwndConsole, nCmdShow);\r
755   }\r
756   UpdateWindow(hwnd);\r
757 \r
758   return TRUE;\r
759 \r
760 }\r
761 \r
762 \r
763 typedef enum {\r
764   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
765   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
766   ArgSettingsFilename\r
767 } ArgType;\r
768 \r
769 typedef struct {\r
770   char *argName;\r
771   ArgType argType;\r
772   /***\r
773   union {\r
774     String *pString;       // ArgString\r
775     int *pInt;             // ArgInt\r
776     float *pFloat;         // ArgFloat\r
777     Boolean *pBoolean;     // ArgBoolean\r
778     COLORREF *pColor;      // ArgColor\r
779     ColorClass cc;         // ArgAttribs\r
780     String *pFilename;     // ArgFilename\r
781     BoardSize *pBoardSize; // ArgBoardSize\r
782     int whichFont;         // ArgFont\r
783     DCB *pDCB;             // ArgCommSettings\r
784     String *pFilename;     // ArgSettingsFilename\r
785   } argLoc;\r
786   ***/\r
787   LPVOID argLoc;\r
788   BOOL save;\r
789 } ArgDescriptor;\r
790 \r
791 int junk;\r
792 ArgDescriptor argDescriptors[] = {\r
793   /* positional arguments */\r
794   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
795   { "", ArgNone, NULL },\r
796   /* keyword arguments */\r
797   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
798   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
799   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
800   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
801   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
802   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
803   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
804   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
805   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
806   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
807   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
808   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
809   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
810   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
811   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
812   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
813   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
814   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
815     FALSE },\r
816   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
817     FALSE },\r
818   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
819     FALSE },\r
820   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
821   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
822     FALSE },\r
823   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
824   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
825   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
826   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
827   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
828   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
829   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
830   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
831   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
832   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
833   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
834   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
835   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
836   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
837   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
838   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
839   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
840   /*!!bitmapDirectory?*/\r
841   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
842   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
843   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
844   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
845   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
846   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
847   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
848   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
849   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
850   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
851   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
852   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
853   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
854   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
855   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
856   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
857   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
858   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
859   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
860   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
861   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
862   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
863   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
864   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
865   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
866   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
867   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
868   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
869   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
870   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
871   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
872   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
873   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
874   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
875   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
876   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
877   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
878   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
879   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
880   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
881   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
882   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
883   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
884   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
885   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
886   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
887   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
888   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
889   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
890   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
891   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
892   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
893   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
894   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
895   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
896   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
897   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
898   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
899   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
900   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
901   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
902   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
903   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
904   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
905   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
906   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
907   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
908   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
909   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
910   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
911   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
912   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
913   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
914   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
915   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
916   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
917   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
918   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
919   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
920   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
921   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
922   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
923   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
924   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
925   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
926   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
927   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
928   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
929   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
930   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
931   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
932   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
933   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
934   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
935     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
936   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
937   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
938   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
939   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
940   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
941   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
942   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
943   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
944     TRUE }, /* must come after all fonts */\r
945   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
946   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
947     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
948   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
949   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
950   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
951   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
952   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
953   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
954   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
955   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
956   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
957   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
958   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
959   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
960   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
961   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
962   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
963   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
964   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
965   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
966   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
967   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
968   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
969   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
970   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
971   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
972   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
973   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
974   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
975   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
976 #if 0\r
977   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
978   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
979 #endif\r
980   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
981   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
982   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
983   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
984   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
985   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
986   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
987   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
988   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
989   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
990   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
991   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
992   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
993   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
994   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
995   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
996   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
997   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
998   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
999   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1000   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1001   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1002   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1003   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1004   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1005   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1006   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1007   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1008   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1009   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1010   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1011   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1012   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1013   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1014   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1015   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1016   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1017   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1018   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1019   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1020   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1021   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1022   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1023   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1024   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1025   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1026   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1027   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1028   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1029   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1030   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1031   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1032   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1033   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1034   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1035   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1036   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1037   { "highlightLastMove", ArgBoolean,\r
1038     (LPVOID) &appData.highlightLastMove, TRUE },\r
1039   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1040   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1041   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1042   { "highlightDragging", ArgBoolean,\r
1043     (LPVOID) &appData.highlightDragging, TRUE },\r
1044   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1045   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1046   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1047   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1048   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1049   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1050   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1051   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1052   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1053   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1054   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1055   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1056   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1057   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1058   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1059   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1060   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1061   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1062   { "soundShout", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1064   { "soundSShout", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1066   { "soundChannel1", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1068   { "soundChannel", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1070   { "soundKibitz", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1072   { "soundTell", ArgFilename,\r
1073     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1074   { "soundChallenge", ArgFilename,\r
1075     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1076   { "soundRequest", ArgFilename,\r
1077     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1078   { "soundSeek", ArgFilename,\r
1079     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1080   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1081   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1082   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1083   { "soundIcsLoss", ArgFilename, \r
1084     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1085   { "soundIcsDraw", ArgFilename, \r
1086     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1087   { "soundIcsUnfinished", ArgFilename, \r
1088     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1089   { "soundIcsAlarm", ArgFilename, \r
1090     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1091   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1092   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1093   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1094   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1095   { "reuseChessPrograms", ArgBoolean,\r
1096     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1097   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1098   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1099   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1100   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1101   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1102   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1103   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1104   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1105   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1106   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1107   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1108   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1109   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1110   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1111   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1112   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1113   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1114   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1115   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1116   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1117   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1118   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1119   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1120   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1121   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1122   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1123   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1124   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1125   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1126   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1127   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1128   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1129   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1130   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1131   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1132   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1133   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1134     TRUE },\r
1135   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1136     TRUE },\r
1137   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1138   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1139   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1140   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1141   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1142   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1143   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1144   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1145   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1146   /* [AS] New features */\r
1147   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1148   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1149   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1150   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1151   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1152   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1153   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1154   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1155   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1156   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1157   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1158   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1159   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1160   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1161   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1162   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1163   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1164   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1165   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1166   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1167   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1168   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1169   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1170   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1171   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1172   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1173   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1174   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1175   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1176   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1177   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1178   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1179   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1180   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1181   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1182   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1183   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1184   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1185   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1186   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1187   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1188   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1189   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1190   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1191   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1192   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1193   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1194   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1195   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1196   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1197 \r
1198   /* [AS] Layout stuff */\r
1199   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1200   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1201   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1202   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1203   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1204 \r
1205   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1206   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1207   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1208   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1209   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1210 \r
1211   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1212   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1213   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1214   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1215   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1216 \r
1217   /* [HGM] board-size, adjudication and misc. options */\r
1218   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1219   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1220   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1221   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1222   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1223   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1224   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1225   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1226   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1227   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1228   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1229   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1230   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1231   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1232   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1233   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1234   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1235   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1236   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1237   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1238   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1239   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1240   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1241   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1242   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1243   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1244   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1245   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1246   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1247 \r
1248 #ifdef ZIPPY\r
1249   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1250   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1251   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1254   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1255   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1258   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1259   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1260   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1261   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1262     FALSE },\r
1263   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1264   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1265   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1266   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1269   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1270     FALSE },\r
1271   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1272   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1273   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1275   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1276   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1277   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1278   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1281   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1282   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1285   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1286   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1287   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1288   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1289 #endif\r
1290   /* [HGM] options for broadcasting and time odds */\r
1291   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1292   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1293   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1294   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1295   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1296   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1297   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1298   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1299   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1300   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1301   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1302   { NULL, ArgNone, NULL, FALSE }\r
1303 };\r
1304 \r
1305 \r
1306 /* Kludge for indirection files on command line */\r
1307 char* lastIndirectionFilename;\r
1308 ArgDescriptor argDescriptorIndirection =\r
1309 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1310 \r
1311 \r
1312 VOID\r
1313 ExitArgError(char *msg, char *badArg)\r
1314 {\r
1315   char buf[MSG_SIZ];\r
1316 \r
1317   sprintf(buf, "%s %s", msg, badArg);\r
1318   DisplayFatalError(buf, 0, 2);\r
1319   exit(2);\r
1320 }\r
1321 \r
1322 /* Command line font name parser.  NULL name means do nothing.\r
1323    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1324    For backward compatibility, syntax without the colon is also\r
1325    accepted, but font names with digits in them won't work in that case.\r
1326 */\r
1327 VOID\r
1328 ParseFontName(char *name, MyFontParams *mfp)\r
1329 {\r
1330   char *p, *q;\r
1331   if (name == NULL) return;\r
1332   p = name;\r
1333   q = strchr(p, ':');\r
1334   if (q) {\r
1335     if (q - p >= sizeof(mfp->faceName))\r
1336       ExitArgError("Font name too long:", name);\r
1337     memcpy(mfp->faceName, p, q - p);\r
1338     mfp->faceName[q - p] = NULLCHAR;\r
1339     p = q + 1;\r
1340   } else {\r
1341     q = mfp->faceName;\r
1342     while (*p && !isdigit(*p)) {\r
1343       *q++ = *p++;\r
1344       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1345         ExitArgError("Font name too long:", name);\r
1346     }\r
1347     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1348     *q = NULLCHAR;\r
1349   }\r
1350   if (!*p) ExitArgError("Font point size missing:", name);\r
1351   mfp->pointSize = (float) atof(p);\r
1352   mfp->bold = (strchr(p, 'b') != NULL);\r
1353   mfp->italic = (strchr(p, 'i') != NULL);\r
1354   mfp->underline = (strchr(p, 'u') != NULL);\r
1355   mfp->strikeout = (strchr(p, 's') != NULL);\r
1356 }\r
1357 \r
1358 /* Color name parser.\r
1359    X version accepts X color names, but this one\r
1360    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1361 COLORREF\r
1362 ParseColorName(char *name)\r
1363 {\r
1364   int red, green, blue, count;\r
1365   char buf[MSG_SIZ];\r
1366 \r
1367   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1368   if (count != 3) {\r
1369     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1370       &red, &green, &blue);\r
1371   }\r
1372   if (count != 3) {\r
1373     sprintf(buf, "Can't parse color name %s", name);\r
1374     DisplayError(buf, 0);\r
1375     return RGB(0, 0, 0);\r
1376   }\r
1377   return PALETTERGB(red, green, blue);\r
1378 }\r
1379 \r
1380 \r
1381 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1382 {\r
1383   char *e = argValue;\r
1384   int eff = 0;\r
1385 \r
1386   while (*e) {\r
1387     if (*e == 'b')      eff |= CFE_BOLD;\r
1388     else if (*e == 'i') eff |= CFE_ITALIC;\r
1389     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1390     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1391     else if (*e == '#' || isdigit(*e)) break;\r
1392     e++;\r
1393   }\r
1394   *effects = eff;\r
1395   *color   = ParseColorName(e);\r
1396 }\r
1397 \r
1398 \r
1399 BoardSize\r
1400 ParseBoardSize(char *name)\r
1401 {\r
1402   BoardSize bs = SizeTiny;\r
1403   while (sizeInfo[bs].name != NULL) {\r
1404     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1405     bs++;\r
1406   }\r
1407   ExitArgError("Unrecognized board size value", name);\r
1408   return bs; /* not reached */\r
1409 }\r
1410 \r
1411 \r
1412 char\r
1413 StringGet(void *getClosure)\r
1414 {\r
1415   char **p = (char **) getClosure;\r
1416   return *((*p)++);\r
1417 }\r
1418 \r
1419 char\r
1420 FileGet(void *getClosure)\r
1421 {\r
1422   int c;\r
1423   FILE* f = (FILE*) getClosure;\r
1424 \r
1425   c = getc(f);\r
1426   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1427   if (c == EOF)\r
1428     return NULLCHAR;\r
1429   else\r
1430     return (char) c;\r
1431 }\r
1432 \r
1433 /* Parse settings file named "name". If file found, return the\r
1434    full name in fullname and return TRUE; else return FALSE */\r
1435 BOOLEAN\r
1436 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1437 {\r
1438   char *dummy;\r
1439   FILE *f;\r
1440   int ok; char buf[MSG_SIZ];\r
1441 \r
1442   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1443   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1444     sprintf(buf, "%s.ini", name);\r
1445     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1446   }\r
1447   if (ok) {\r
1448     f = fopen(fullname, "r");\r
1449     if (f != NULL) {\r
1450       ParseArgs(FileGet, f);\r
1451       fclose(f);\r
1452       return TRUE;\r
1453     }\r
1454   }\r
1455   return FALSE;\r
1456 }\r
1457 \r
1458 VOID\r
1459 ParseArgs(GetFunc get, void *cl)\r
1460 {\r
1461   char argName[ARG_MAX];\r
1462   char argValue[ARG_MAX];\r
1463   ArgDescriptor *ad;\r
1464   char start;\r
1465   char *q;\r
1466   int i, octval;\r
1467   char ch;\r
1468   int posarg = 0;\r
1469 \r
1470   ch = get(cl);\r
1471   for (;;) {\r
1472     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1473     if (ch == NULLCHAR) break;\r
1474     if (ch == ';') {\r
1475       /* Comment to end of line */\r
1476       ch = get(cl);\r
1477       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1478       continue;\r
1479     } else if (ch == '/' || ch == '-') {\r
1480       /* Switch */\r
1481       q = argName;\r
1482       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1483              ch != '\n' && ch != '\t') {\r
1484         *q++ = ch;\r
1485         ch = get(cl);\r
1486       }\r
1487       *q = NULLCHAR;\r
1488 \r
1489       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1490         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1491 \r
1492       if (ad->argName == NULL)\r
1493         ExitArgError("Unrecognized argument", argName);\r
1494 \r
1495     } else if (ch == '@') {\r
1496       /* Indirection file */\r
1497       ad = &argDescriptorIndirection;\r
1498       ch = get(cl);\r
1499     } else {\r
1500       /* Positional argument */\r
1501       ad = &argDescriptors[posarg++];\r
1502       strcpy(argName, ad->argName);\r
1503     }\r
1504 \r
1505     if (ad->argType == ArgTrue) {\r
1506       *(Boolean *) ad->argLoc = TRUE;\r
1507       continue;\r
1508     }\r
1509     if (ad->argType == ArgFalse) {\r
1510       *(Boolean *) ad->argLoc = FALSE;\r
1511       continue;\r
1512     }\r
1513 \r
1514     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1515     if (ch == NULLCHAR || ch == '\n') {\r
1516       ExitArgError("No value provided for argument", argName);\r
1517     }\r
1518     q = argValue;\r
1519     if (ch == '{') {\r
1520       // Quoting with { }.  No characters have to (or can) be escaped.\r
1521       // Thus the string cannot contain a '}' character.\r
1522       start = ch;\r
1523       ch = get(cl);\r
1524       while (start) {\r
1525         switch (ch) {\r
1526         case NULLCHAR:\r
1527           start = NULLCHAR;\r
1528           break;\r
1529           \r
1530         case '}':\r
1531           ch = get(cl);\r
1532           start = NULLCHAR;\r
1533           break;\r
1534 \r
1535         default:\r
1536           *q++ = ch;\r
1537           ch = get(cl);\r
1538           break;\r
1539         }\r
1540       }   \r
1541     } else if (ch == '\'' || ch == '"') {\r
1542       // Quoting with ' ' or " ", with \ as escape character.\r
1543       // Inconvenient for long strings that may contain Windows filenames.\r
1544       start = ch;\r
1545       ch = get(cl);\r
1546       while (start) {\r
1547         switch (ch) {\r
1548         case NULLCHAR:\r
1549           start = NULLCHAR;\r
1550           break;\r
1551 \r
1552         default:\r
1553         not_special:\r
1554           *q++ = ch;\r
1555           ch = get(cl);\r
1556           break;\r
1557 \r
1558         case '\'':\r
1559         case '\"':\r
1560           if (ch == start) {\r
1561             ch = get(cl);\r
1562             start = NULLCHAR;\r
1563             break;\r
1564           } else {\r
1565             goto not_special;\r
1566           }\r
1567 \r
1568         case '\\':\r
1569           if (ad->argType == ArgFilename\r
1570               || ad->argType == ArgSettingsFilename) {\r
1571               goto not_special;\r
1572           }\r
1573           ch = get(cl);\r
1574           switch (ch) {\r
1575           case NULLCHAR:\r
1576             ExitArgError("Incomplete \\ escape in value for", argName);\r
1577             break;\r
1578           case 'n':\r
1579             *q++ = '\n';\r
1580             ch = get(cl);\r
1581             break;\r
1582           case 'r':\r
1583             *q++ = '\r';\r
1584             ch = get(cl);\r
1585             break;\r
1586           case 't':\r
1587             *q++ = '\t';\r
1588             ch = get(cl);\r
1589             break;\r
1590           case 'b':\r
1591             *q++ = '\b';\r
1592             ch = get(cl);\r
1593             break;\r
1594           case 'f':\r
1595             *q++ = '\f';\r
1596             ch = get(cl);\r
1597             break;\r
1598           default:\r
1599             octval = 0;\r
1600             for (i = 0; i < 3; i++) {\r
1601               if (ch >= '0' && ch <= '7') {\r
1602                 octval = octval*8 + (ch - '0');\r
1603                 ch = get(cl);\r
1604               } else {\r
1605                 break;\r
1606               }\r
1607             }\r
1608             if (i > 0) {\r
1609               *q++ = (char) octval;\r
1610             } else {\r
1611               *q++ = ch;\r
1612               ch = get(cl);\r
1613             }\r
1614             break;\r
1615           }\r
1616           break;\r
1617         }\r
1618       }\r
1619     } else {\r
1620       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1621         *q++ = ch;\r
1622         ch = get(cl);\r
1623       }\r
1624     }\r
1625     *q = NULLCHAR;\r
1626 \r
1627     switch (ad->argType) {\r
1628     case ArgInt:\r
1629       *(int *) ad->argLoc = atoi(argValue);\r
1630       break;\r
1631 \r
1632     case ArgFloat:\r
1633       *(float *) ad->argLoc = (float) atof(argValue);\r
1634       break;\r
1635 \r
1636     case ArgString:\r
1637     case ArgFilename:\r
1638       *(char **) ad->argLoc = strdup(argValue);\r
1639       break;\r
1640 \r
1641     case ArgSettingsFilename:\r
1642       {\r
1643         char fullname[MSG_SIZ];\r
1644         if (ParseSettingsFile(argValue, fullname)) {\r
1645           if (ad->argLoc != NULL) {\r
1646             *(char **) ad->argLoc = strdup(fullname);\r
1647           }\r
1648         } else {\r
1649           if (ad->argLoc != NULL) {\r
1650           } else {\r
1651             ExitArgError("Failed to open indirection file", argValue);\r
1652           }\r
1653         }\r
1654       }\r
1655       break;\r
1656 \r
1657     case ArgBoolean:\r
1658       switch (argValue[0]) {\r
1659       case 't':\r
1660       case 'T':\r
1661         *(Boolean *) ad->argLoc = TRUE;\r
1662         break;\r
1663       case 'f':\r
1664       case 'F':\r
1665         *(Boolean *) ad->argLoc = FALSE;\r
1666         break;\r
1667       default:\r
1668         ExitArgError("Unrecognized boolean argument value", argValue);\r
1669         break;\r
1670       }\r
1671       break;\r
1672 \r
1673     case ArgColor:\r
1674       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1675       break;\r
1676 \r
1677     case ArgAttribs: {\r
1678       ColorClass cc = (ColorClass)ad->argLoc;\r
1679       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1680       }\r
1681       break;\r
1682       \r
1683     case ArgBoardSize:\r
1684       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1685       break;\r
1686 \r
1687     case ArgFont:\r
1688       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1689       break;\r
1690 \r
1691     case ArgCommSettings:\r
1692       ParseCommSettings(argValue, &dcb);\r
1693       break;\r
1694 \r
1695     case ArgNone:\r
1696       ExitArgError("Unrecognized argument", argValue);\r
1697       break;\r
1698     case ArgTrue:\r
1699     case ArgFalse: ;\r
1700     }\r
1701   }\r
1702 }\r
1703 \r
1704 VOID\r
1705 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1706 {\r
1707   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1708   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1709   DeleteDC(hdc);\r
1710   lf->lfWidth = 0;\r
1711   lf->lfEscapement = 0;\r
1712   lf->lfOrientation = 0;\r
1713   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1714   lf->lfItalic = mfp->italic;\r
1715   lf->lfUnderline = mfp->underline;\r
1716   lf->lfStrikeOut = mfp->strikeout;\r
1717   lf->lfCharSet = DEFAULT_CHARSET;\r
1718   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1719   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1720   lf->lfQuality = DEFAULT_QUALITY;\r
1721   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1722   strcpy(lf->lfFaceName, mfp->faceName);\r
1723 }\r
1724 \r
1725 VOID\r
1726 CreateFontInMF(MyFont *mf)\r
1727 {\r
1728   LFfromMFP(&mf->lf, &mf->mfp);\r
1729   if (mf->hf) DeleteObject(mf->hf);\r
1730   mf->hf = CreateFontIndirect(&mf->lf);\r
1731 }\r
1732 \r
1733 VOID\r
1734 SetDefaultTextAttribs()\r
1735 {\r
1736   ColorClass cc;\r
1737   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1738     ParseAttribs(&textAttribs[cc].color, \r
1739                  &textAttribs[cc].effects, \r
1740                  defaultTextAttribs[cc]);\r
1741   }\r
1742 }\r
1743 \r
1744 VOID\r
1745 SetDefaultSounds()\r
1746 {\r
1747   ColorClass cc;\r
1748   SoundClass sc;\r
1749   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1750     textAttribs[cc].sound.name = strdup("");\r
1751     textAttribs[cc].sound.data = NULL;\r
1752   }\r
1753   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1754     sounds[sc].name = strdup("");\r
1755     sounds[sc].data = NULL;\r
1756   }\r
1757   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1758 }\r
1759 \r
1760 VOID\r
1761 LoadAllSounds()\r
1762 {\r
1763   ColorClass cc;\r
1764   SoundClass sc;\r
1765   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1766     MyLoadSound(&textAttribs[cc].sound);\r
1767   }\r
1768   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1769     MyLoadSound(&sounds[sc]);\r
1770   }\r
1771 }\r
1772 \r
1773 VOID\r
1774 InitAppData(LPSTR lpCmdLine)\r
1775 {\r
1776   int i, j;\r
1777   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1778   char *dummy, *p;\r
1779 \r
1780   programName = szAppName;\r
1781 \r
1782   /* Initialize to defaults */\r
1783   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1784   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1785   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1786   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1787   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1788   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1789   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1790   SetDefaultTextAttribs();\r
1791   SetDefaultSounds();\r
1792   appData.movesPerSession = MOVES_PER_SESSION;\r
1793   appData.initString = INIT_STRING;\r
1794   appData.secondInitString = INIT_STRING;\r
1795   appData.firstComputerString = COMPUTER_STRING;\r
1796   appData.secondComputerString = COMPUTER_STRING;\r
1797   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1798   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1799   appData.firstPlaysBlack = FALSE;\r
1800   appData.noChessProgram = FALSE;\r
1801   chessProgram = FALSE;\r
1802   appData.firstHost = FIRST_HOST;\r
1803   appData.secondHost = SECOND_HOST;\r
1804   appData.firstDirectory = FIRST_DIRECTORY;\r
1805   appData.secondDirectory = SECOND_DIRECTORY;\r
1806   appData.bitmapDirectory = "";\r
1807   appData.remoteShell = REMOTE_SHELL;\r
1808   appData.remoteUser = "";\r
1809   appData.timeDelay = TIME_DELAY;\r
1810   appData.timeControl = TIME_CONTROL;\r
1811   appData.timeIncrement = TIME_INCREMENT;\r
1812   appData.icsActive = FALSE;\r
1813   appData.icsHost = "";\r
1814   appData.icsPort = ICS_PORT;\r
1815   appData.icsCommPort = ICS_COMM_PORT;\r
1816   appData.icsLogon = ICS_LOGON;\r
1817   appData.icsHelper = "";\r
1818   appData.useTelnet = FALSE;\r
1819   appData.telnetProgram = TELNET_PROGRAM;\r
1820   appData.gateway = "";\r
1821   appData.loadGameFile = "";\r
1822   appData.loadGameIndex = 0;\r
1823   appData.saveGameFile = "";\r
1824   appData.autoSaveGames = FALSE;\r
1825   appData.loadPositionFile = "";\r
1826   appData.loadPositionIndex = 1;\r
1827   appData.savePositionFile = "";\r
1828   appData.matchMode = FALSE;\r
1829   appData.matchGames = 0;\r
1830   appData.monoMode = FALSE;\r
1831   appData.debugMode = FALSE;\r
1832   appData.clockMode = TRUE;\r
1833   boardSize = (BoardSize) -1; /* determine by screen size */\r
1834   appData.Iconic = FALSE; /*unused*/\r
1835   appData.searchTime = "";\r
1836   appData.searchDepth = 0;\r
1837   appData.showCoords = FALSE;\r
1838   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1839   appData.autoCallFlag = FALSE;\r
1840   appData.flipView = FALSE;\r
1841   appData.autoFlipView = TRUE;\r
1842   appData.cmailGameName = "";\r
1843   appData.alwaysPromoteToQueen = FALSE;\r
1844   appData.oldSaveStyle = FALSE;\r
1845   appData.quietPlay = FALSE;\r
1846   appData.showThinking = FALSE;\r
1847   appData.ponderNextMove = TRUE;\r
1848   appData.periodicUpdates = TRUE;\r
1849   appData.popupExitMessage = TRUE;\r
1850   appData.popupMoveErrors = FALSE;\r
1851   appData.autoObserve = FALSE;\r
1852   appData.autoComment = FALSE;\r
1853   appData.animate = TRUE;\r
1854   appData.animSpeed = 10;\r
1855   appData.animateDragging = TRUE;\r
1856   appData.highlightLastMove = TRUE;\r
1857   appData.getMoveList = TRUE;\r
1858   appData.testLegality = TRUE;\r
1859   appData.premove = TRUE;\r
1860   appData.premoveWhite = FALSE;\r
1861   appData.premoveWhiteText = "";\r
1862   appData.premoveBlack = FALSE;\r
1863   appData.premoveBlackText = "";\r
1864   appData.icsAlarm = TRUE;\r
1865   appData.icsAlarmTime = 5000;\r
1866   appData.autoRaiseBoard = TRUE;\r
1867   appData.localLineEditing = TRUE;\r
1868   appData.colorize = TRUE;\r
1869   appData.reuseFirst = TRUE;\r
1870   appData.reuseSecond = TRUE;\r
1871   appData.blindfold = FALSE;\r
1872   appData.icsEngineAnalyze = FALSE;\r
1873   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1874   dcb.DCBlength = sizeof(DCB);\r
1875   dcb.BaudRate = 9600;\r
1876   dcb.fBinary = TRUE;\r
1877   dcb.fParity = FALSE;\r
1878   dcb.fOutxCtsFlow = FALSE;\r
1879   dcb.fOutxDsrFlow = FALSE;\r
1880   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1881   dcb.fDsrSensitivity = FALSE;\r
1882   dcb.fTXContinueOnXoff = TRUE;\r
1883   dcb.fOutX = FALSE;\r
1884   dcb.fInX = FALSE;\r
1885   dcb.fNull = FALSE;\r
1886   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1887   dcb.fAbortOnError = FALSE;\r
1888   dcb.ByteSize = 7;\r
1889   dcb.Parity = SPACEPARITY;\r
1890   dcb.StopBits = ONESTOPBIT;\r
1891   settingsFileName = SETTINGS_FILE;\r
1892   saveSettingsOnExit = TRUE;\r
1893   boardX = CW_USEDEFAULT;\r
1894   boardY = CW_USEDEFAULT;\r
1895   consoleX = CW_USEDEFAULT; \r
1896   consoleY = CW_USEDEFAULT; \r
1897   consoleW = CW_USEDEFAULT;\r
1898   consoleH = CW_USEDEFAULT;\r
1899   analysisX = CW_USEDEFAULT; \r
1900   analysisY = CW_USEDEFAULT; \r
1901   analysisW = CW_USEDEFAULT;\r
1902   analysisH = CW_USEDEFAULT;\r
1903   commentX = CW_USEDEFAULT; \r
1904   commentY = CW_USEDEFAULT; \r
1905   commentW = CW_USEDEFAULT;\r
1906   commentH = CW_USEDEFAULT;\r
1907   editTagsX = CW_USEDEFAULT; \r
1908   editTagsY = CW_USEDEFAULT; \r
1909   editTagsW = CW_USEDEFAULT;\r
1910   editTagsH = CW_USEDEFAULT;\r
1911   gameListX = CW_USEDEFAULT; \r
1912   gameListY = CW_USEDEFAULT; \r
1913   gameListW = CW_USEDEFAULT;\r
1914   gameListH = CW_USEDEFAULT;\r
1915   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1916   icsNames = ICS_NAMES;\r
1917   firstChessProgramNames = FCP_NAMES;\r
1918   secondChessProgramNames = SCP_NAMES;\r
1919   appData.initialMode = "";\r
1920   appData.variant = "normal";\r
1921   appData.firstProtocolVersion = PROTOVER;\r
1922   appData.secondProtocolVersion = PROTOVER;\r
1923   appData.showButtonBar = TRUE;\r
1924 \r
1925    /* [AS] New properties (see comments in header file) */\r
1926   appData.firstScoreIsAbsolute = FALSE;\r
1927   appData.secondScoreIsAbsolute = FALSE;\r
1928   appData.saveExtendedInfoInPGN = FALSE;\r
1929   appData.hideThinkingFromHuman = FALSE;\r
1930   appData.liteBackTextureFile = "";\r
1931   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1932   appData.darkBackTextureFile = "";\r
1933   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1934   appData.renderPiecesWithFont = "";\r
1935   appData.fontToPieceTable = "";\r
1936   appData.fontBackColorWhite = 0;\r
1937   appData.fontForeColorWhite = 0;\r
1938   appData.fontBackColorBlack = 0;\r
1939   appData.fontForeColorBlack = 0;\r
1940   appData.fontPieceSize = 80;\r
1941   appData.overrideLineGap = 1;\r
1942   appData.adjudicateLossThreshold = 0;\r
1943   appData.delayBeforeQuit = 0;\r
1944   appData.delayAfterQuit = 0;\r
1945   appData.nameOfDebugFile = "winboard.debug";\r
1946   appData.pgnEventHeader = "Computer Chess Game";\r
1947   appData.defaultFrcPosition = -1;\r
1948   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1949   appData.saveOutOfBookInfo = TRUE;\r
1950   appData.showEvalInMoveHistory = TRUE;\r
1951   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1952   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1953   appData.highlightMoveWithArrow = FALSE;\r
1954   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1955   appData.useStickyWindows = TRUE;\r
1956   appData.adjudicateDrawMoves = 0;\r
1957   appData.autoDisplayComment = TRUE;\r
1958   appData.autoDisplayTags = TRUE;\r
1959   appData.firstIsUCI = FALSE;\r
1960   appData.secondIsUCI = FALSE;\r
1961   appData.firstHasOwnBookUCI = TRUE;\r
1962   appData.secondHasOwnBookUCI = TRUE;\r
1963   appData.polyglotDir = "";\r
1964   appData.usePolyglotBook = FALSE;\r
1965   appData.polyglotBook = "";\r
1966   appData.defaultHashSize = 64;\r
1967   appData.defaultCacheSizeEGTB = 4;\r
1968   appData.defaultPathEGTB = "c:\\egtb";\r
1969   appData.firstOptions = "";\r
1970   appData.secondOptions = "";\r
1971 \r
1972   InitWindowPlacement( &wpMoveHistory );\r
1973   InitWindowPlacement( &wpEvalGraph );\r
1974   InitWindowPlacement( &wpEngineOutput );\r
1975 \r
1976   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1977   appData.NrFiles      = -1;\r
1978   appData.NrRanks      = -1;\r
1979   appData.holdingsSize = -1;\r
1980   appData.testClaims   = FALSE;\r
1981   appData.checkMates   = FALSE;\r
1982   appData.materialDraws= FALSE;\r
1983   appData.trivialDraws = FALSE;\r
1984   appData.ruleMoves    = 51;\r
1985   appData.drawRepeats  = 6;\r
1986   appData.matchPause   = 10000;\r
1987   appData.alphaRank    = FALSE;\r
1988   appData.allWhite     = FALSE;\r
1989   appData.upsideDown   = FALSE;\r
1990   appData.serverPause  = 15;\r
1991   appData.serverMovesName   = NULL;\r
1992   appData.suppressLoadMoves = FALSE;\r
1993   appData.firstTimeOdds  = 1;\r
1994   appData.secondTimeOdds = 1;\r
1995   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1996   appData.secondAccumulateTC = 1;\r
1997   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1998   appData.secondNPS = -1;\r
1999   appData.engineComments = 1;\r
2000   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2001   appData.egtFormats = "";\r
2002 \r
2003 #ifdef ZIPPY\r
2004   appData.zippyTalk = ZIPPY_TALK;\r
2005   appData.zippyPlay = ZIPPY_PLAY;\r
2006   appData.zippyLines = ZIPPY_LINES;\r
2007   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2008   appData.zippyPassword = ZIPPY_PASSWORD;\r
2009   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2010   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2011   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2012   appData.zippyUseI = ZIPPY_USE_I;\r
2013   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2014   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2015   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2016   appData.zippyGameStart = ZIPPY_GAME_START;\r
2017   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2018   appData.zippyAbort = ZIPPY_ABORT;\r
2019   appData.zippyVariants = ZIPPY_VARIANTS;\r
2020   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2021   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2022 #endif\r
2023 \r
2024   /* Point font array elements to structures and\r
2025      parse default font names */\r
2026   for (i=0; i<NUM_FONTS; i++) {\r
2027     for (j=0; j<NUM_SIZES; j++) {\r
2028       font[j][i] = &fontRec[j][i];\r
2029       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2030     }\r
2031   }\r
2032   \r
2033   /* Parse default settings file if any */\r
2034   if (ParseSettingsFile(settingsFileName, buf)) {\r
2035     settingsFileName = strdup(buf);\r
2036   }\r
2037 \r
2038   /* Parse command line */\r
2039   ParseArgs(StringGet, &lpCmdLine);\r
2040 \r
2041   /* [HGM] make sure board size is acceptable */\r
2042   if(appData.NrFiles > BOARD_SIZE ||\r
2043      appData.NrRanks > BOARD_SIZE   )\r
2044       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2045 \r
2046   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2047    * with options from the command line, we now make an even higher priority\r
2048    * overrule by WB options attached to the engine command line. This so that\r
2049    * tournament managers can use WB options (such as /timeOdds) that follow\r
2050    * the engines.\r
2051    */\r
2052   if(appData.firstChessProgram != NULL) {\r
2053       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2054       static char *f = "first";\r
2055       char buf[MSG_SIZ], *q = buf;\r
2056       if(p != NULL) { // engine command line contains WinBoard options\r
2057           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2058           ParseArgs(StringGet, &q);\r
2059           p[-1] = 0; // cut them offengine command line\r
2060       }\r
2061   }\r
2062   // now do same for second chess program\r
2063   if(appData.secondChessProgram != NULL) {\r
2064       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2065       static char *s = "second";\r
2066       char buf[MSG_SIZ], *q = buf;\r
2067       if(p != NULL) { // engine command line contains WinBoard options\r
2068           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2069           ParseArgs(StringGet, &q);\r
2070           p[-1] = 0; // cut them offengine command line\r
2071       }\r
2072   }\r
2073 \r
2074 \r
2075   /* Propagate options that affect others */\r
2076   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2077   if (appData.icsActive || appData.noChessProgram) {\r
2078      chessProgram = FALSE;  /* not local chess program mode */\r
2079   }\r
2080 \r
2081   /* Open startup dialog if needed */\r
2082   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2083       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2084       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2085                         *appData.secondChessProgram == NULLCHAR))) {\r
2086     FARPROC lpProc;\r
2087     \r
2088     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2089     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2090     FreeProcInstance(lpProc);\r
2091   }\r
2092 \r
2093   /* Make sure save files land in the right (?) directory */\r
2094   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2095     appData.saveGameFile = strdup(buf);\r
2096   }\r
2097   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2098     appData.savePositionFile = strdup(buf);\r
2099   }\r
2100 \r
2101   /* Finish initialization for fonts and sounds */\r
2102   for (i=0; i<NUM_FONTS; i++) {\r
2103     for (j=0; j<NUM_SIZES; j++) {\r
2104       CreateFontInMF(font[j][i]);\r
2105     }\r
2106   }\r
2107   /* xboard, and older WinBoards, controlled the move sound with the\r
2108      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2109      always turn the option on (so that the backend will call us),\r
2110      then let the user turn the sound off by setting it to silence if\r
2111      desired.  To accommodate old winboard.ini files saved by old\r
2112      versions of WinBoard, we also turn off the sound if the option\r
2113      was initially set to false. */\r
2114   if (!appData.ringBellAfterMoves) {\r
2115     sounds[(int)SoundMove].name = strdup("");\r
2116     appData.ringBellAfterMoves = TRUE;\r
2117   }\r
2118   GetCurrentDirectory(MSG_SIZ, currDir);\r
2119   SetCurrentDirectory(installDir);\r
2120   LoadAllSounds();\r
2121   SetCurrentDirectory(currDir);\r
2122 \r
2123   p = icsTextMenuString;\r
2124   if (p[0] == '@') {\r
2125     FILE* f = fopen(p + 1, "r");\r
2126     if (f == NULL) {\r
2127       DisplayFatalError(p + 1, errno, 2);\r
2128       return;\r
2129     }\r
2130     i = fread(buf, 1, sizeof(buf)-1, f);\r
2131     fclose(f);\r
2132     buf[i] = NULLCHAR;\r
2133     p = buf;\r
2134   }\r
2135   ParseIcsTextMenu(strdup(p));\r
2136 }\r
2137 \r
2138 \r
2139 VOID\r
2140 InitMenuChecks()\r
2141 {\r
2142   HMENU hmenu = GetMenu(hwndMain);\r
2143 \r
2144   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2145                         MF_BYCOMMAND|((appData.icsActive &&\r
2146                                        *appData.icsCommPort != NULLCHAR) ?\r
2147                                       MF_ENABLED : MF_GRAYED));\r
2148   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2149                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2150                                      MF_CHECKED : MF_UNCHECKED));\r
2151 }\r
2152 \r
2153 \r
2154 VOID\r
2155 SaveSettings(char* name)\r
2156 {\r
2157   FILE *f;\r
2158   ArgDescriptor *ad;\r
2159   WINDOWPLACEMENT wp;\r
2160   char dir[MSG_SIZ];\r
2161 \r
2162   if (!hwndMain) return;\r
2163 \r
2164   GetCurrentDirectory(MSG_SIZ, dir);\r
2165   SetCurrentDirectory(installDir);\r
2166   f = fopen(name, "w");\r
2167   SetCurrentDirectory(dir);\r
2168   if (f == NULL) {\r
2169     DisplayError(name, errno);\r
2170     return;\r
2171   }\r
2172   fprintf(f, ";\n");\r
2173   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2174   fprintf(f, ";\n");\r
2175   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2176   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2177   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2178   fprintf(f, ";\n");\r
2179 \r
2180   wp.length = sizeof(WINDOWPLACEMENT);\r
2181   GetWindowPlacement(hwndMain, &wp);\r
2182   boardX = wp.rcNormalPosition.left;\r
2183   boardY = wp.rcNormalPosition.top;\r
2184 \r
2185   if (hwndConsole) {\r
2186     GetWindowPlacement(hwndConsole, &wp);\r
2187     consoleX = wp.rcNormalPosition.left;\r
2188     consoleY = wp.rcNormalPosition.top;\r
2189     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2190     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2191   }\r
2192 \r
2193   if (analysisDialog) {\r
2194     GetWindowPlacement(analysisDialog, &wp);\r
2195     analysisX = wp.rcNormalPosition.left;\r
2196     analysisY = wp.rcNormalPosition.top;\r
2197     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2198     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2199   }\r
2200 \r
2201   if (commentDialog) {\r
2202     GetWindowPlacement(commentDialog, &wp);\r
2203     commentX = wp.rcNormalPosition.left;\r
2204     commentY = wp.rcNormalPosition.top;\r
2205     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2206     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2207   }\r
2208 \r
2209   if (editTagsDialog) {\r
2210     GetWindowPlacement(editTagsDialog, &wp);\r
2211     editTagsX = wp.rcNormalPosition.left;\r
2212     editTagsY = wp.rcNormalPosition.top;\r
2213     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2214     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2215   }\r
2216 \r
2217   if (gameListDialog) {\r
2218     GetWindowPlacement(gameListDialog, &wp);\r
2219     gameListX = wp.rcNormalPosition.left;\r
2220     gameListY = wp.rcNormalPosition.top;\r
2221     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2222     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2223   }\r
2224 \r
2225   /* [AS] Move history */\r
2226   wpMoveHistory.visible = MoveHistoryIsUp();\r
2227   \r
2228   if( moveHistoryDialog ) {\r
2229     GetWindowPlacement(moveHistoryDialog, &wp);\r
2230     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2231     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2232     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2233     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2234   }\r
2235 \r
2236   /* [AS] Eval graph */\r
2237   wpEvalGraph.visible = EvalGraphIsUp();\r
2238 \r
2239   if( evalGraphDialog ) {\r
2240     GetWindowPlacement(evalGraphDialog, &wp);\r
2241     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2242     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2243     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2244     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2245   }\r
2246 \r
2247   /* [AS] Engine output */\r
2248   wpEngineOutput.visible = EngineOutputIsUp();\r
2249 \r
2250   if( engineOutputDialog ) {\r
2251     GetWindowPlacement(engineOutputDialog, &wp);\r
2252     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2253     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2254     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2255     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2256   }\r
2257 \r
2258   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2259     if (!ad->save) continue;\r
2260     switch (ad->argType) {\r
2261     case ArgString:\r
2262       {\r
2263         char *p = *(char **)ad->argLoc;\r
2264         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2265           /* Quote multiline values or \-containing values\r
2266              with { } if possible */\r
2267           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2268         } else {\r
2269           /* Else quote with " " */\r
2270           fprintf(f, "/%s=\"", ad->argName);\r
2271           while (*p) {\r
2272             if (*p == '\n') fprintf(f, "\n");\r
2273             else if (*p == '\r') fprintf(f, "\\r");\r
2274             else if (*p == '\t') fprintf(f, "\\t");\r
2275             else if (*p == '\b') fprintf(f, "\\b");\r
2276             else if (*p == '\f') fprintf(f, "\\f");\r
2277             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2278             else if (*p == '\"') fprintf(f, "\\\"");\r
2279             else if (*p == '\\') fprintf(f, "\\\\");\r
2280             else putc(*p, f);\r
2281             p++;\r
2282           }\r
2283           fprintf(f, "\"\n");\r
2284         }\r
2285       }\r
2286       break;\r
2287     case ArgInt:\r
2288       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2289       break;\r
2290     case ArgFloat:\r
2291       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2292       break;\r
2293     case ArgBoolean:\r
2294       fprintf(f, "/%s=%s\n", ad->argName, \r
2295         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2296       break;\r
2297     case ArgTrue:\r
2298       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2299       break;\r
2300     case ArgFalse:\r
2301       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2302       break;\r
2303     case ArgColor:\r
2304       {\r
2305         COLORREF color = *(COLORREF *)ad->argLoc;\r
2306         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2307           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2308       }\r
2309       break;\r
2310     case ArgAttribs:\r
2311       {\r
2312         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2313         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2314           (ta->effects & CFE_BOLD) ? "b" : "",\r
2315           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2316           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2317           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2318           (ta->effects) ? " " : "",\r
2319           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2320       }\r
2321       break;\r
2322     case ArgFilename:\r
2323       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2324         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2325       } else {\r
2326         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2327       }\r
2328       break;\r
2329     case ArgBoardSize:\r
2330       fprintf(f, "/%s=%s\n", ad->argName,\r
2331               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2332       break;\r
2333     case ArgFont:\r
2334       {\r
2335         int bs;\r
2336         for (bs=0; bs<NUM_SIZES; bs++) {\r
2337           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2338           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2339           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2340             ad->argName, mfp->faceName, mfp->pointSize,\r
2341             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2342             mfp->bold ? "b" : "",\r
2343             mfp->italic ? "i" : "",\r
2344             mfp->underline ? "u" : "",\r
2345             mfp->strikeout ? "s" : "");\r
2346         }\r
2347       }\r
2348       break;\r
2349     case ArgCommSettings:\r
2350       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2351     case ArgNone:\r
2352     case ArgSettingsFilename: ;\r
2353     }\r
2354   }\r
2355   fclose(f);\r
2356 }\r
2357 \r
2358 \r
2359 \r
2360 /*---------------------------------------------------------------------------*\\r
2361  *\r
2362  * GDI board drawing routines\r
2363  *\r
2364 \*---------------------------------------------------------------------------*/\r
2365 \r
2366 /* [AS] Draw square using background texture */\r
2367 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2368 {\r
2369     XFORM   x;\r
2370 \r
2371     if( mode == 0 ) {\r
2372         return; /* Should never happen! */\r
2373     }\r
2374 \r
2375     SetGraphicsMode( dst, GM_ADVANCED );\r
2376 \r
2377     switch( mode ) {\r
2378     case 1:\r
2379         /* Identity */\r
2380         break;\r
2381     case 2:\r
2382         /* X reflection */\r
2383         x.eM11 = -1.0;\r
2384         x.eM12 = 0;\r
2385         x.eM21 = 0;\r
2386         x.eM22 = 1.0;\r
2387         x.eDx = (FLOAT) dw + dx - 1;\r
2388         x.eDy = 0;\r
2389         dx = 0;\r
2390         SetWorldTransform( dst, &x );\r
2391         break;\r
2392     case 3:\r
2393         /* Y reflection */\r
2394         x.eM11 = 1.0;\r
2395         x.eM12 = 0;\r
2396         x.eM21 = 0;\r
2397         x.eM22 = -1.0;\r
2398         x.eDx = 0;\r
2399         x.eDy = (FLOAT) dh + dy - 1;\r
2400         dy = 0;\r
2401         SetWorldTransform( dst, &x );\r
2402         break;\r
2403     case 4:\r
2404         /* X/Y flip */\r
2405         x.eM11 = 0;\r
2406         x.eM12 = 1.0;\r
2407         x.eM21 = 1.0;\r
2408         x.eM22 = 0;\r
2409         x.eDx = (FLOAT) dx;\r
2410         x.eDy = (FLOAT) dy;\r
2411         dx = 0;\r
2412         dy = 0;\r
2413         SetWorldTransform( dst, &x );\r
2414         break;\r
2415     }\r
2416 \r
2417     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2418 \r
2419     x.eM11 = 1.0;\r
2420     x.eM12 = 0;\r
2421     x.eM21 = 0;\r
2422     x.eM22 = 1.0;\r
2423     x.eDx = 0;\r
2424     x.eDy = 0;\r
2425     SetWorldTransform( dst, &x );\r
2426 \r
2427     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2428 }\r
2429 \r
2430 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2431 enum {\r
2432     PM_WP = (int) WhitePawn, \r
2433     PM_WN = (int) WhiteKnight, \r
2434     PM_WB = (int) WhiteBishop, \r
2435     PM_WR = (int) WhiteRook, \r
2436     PM_WQ = (int) WhiteQueen, \r
2437     PM_WF = (int) WhiteFerz, \r
2438     PM_WW = (int) WhiteWazir, \r
2439     PM_WE = (int) WhiteAlfil, \r
2440     PM_WM = (int) WhiteMan, \r
2441     PM_WO = (int) WhiteCannon, \r
2442     PM_WU = (int) WhiteUnicorn, \r
2443     PM_WH = (int) WhiteNightrider, \r
2444     PM_WA = (int) WhiteAngel, \r
2445     PM_WC = (int) WhiteMarshall, \r
2446     PM_WAB = (int) WhiteCardinal, \r
2447     PM_WD = (int) WhiteDragon, \r
2448     PM_WL = (int) WhiteLance, \r
2449     PM_WS = (int) WhiteCobra, \r
2450     PM_WV = (int) WhiteFalcon, \r
2451     PM_WSG = (int) WhiteSilver, \r
2452     PM_WG = (int) WhiteGrasshopper, \r
2453     PM_WK = (int) WhiteKing,\r
2454     PM_BP = (int) BlackPawn, \r
2455     PM_BN = (int) BlackKnight, \r
2456     PM_BB = (int) BlackBishop, \r
2457     PM_BR = (int) BlackRook, \r
2458     PM_BQ = (int) BlackQueen, \r
2459     PM_BF = (int) BlackFerz, \r
2460     PM_BW = (int) BlackWazir, \r
2461     PM_BE = (int) BlackAlfil, \r
2462     PM_BM = (int) BlackMan,\r
2463     PM_BO = (int) BlackCannon, \r
2464     PM_BU = (int) BlackUnicorn, \r
2465     PM_BH = (int) BlackNightrider, \r
2466     PM_BA = (int) BlackAngel, \r
2467     PM_BC = (int) BlackMarshall, \r
2468     PM_BG = (int) BlackGrasshopper, \r
2469     PM_BAB = (int) BlackCardinal,\r
2470     PM_BD = (int) BlackDragon,\r
2471     PM_BL = (int) BlackLance,\r
2472     PM_BS = (int) BlackCobra,\r
2473     PM_BV = (int) BlackFalcon,\r
2474     PM_BSG = (int) BlackSilver,\r
2475     PM_BK = (int) BlackKing\r
2476 };\r
2477 \r
2478 static HFONT hPieceFont = NULL;\r
2479 static HBITMAP hPieceMask[(int) EmptySquare];\r
2480 static HBITMAP hPieceFace[(int) EmptySquare];\r
2481 static int fontBitmapSquareSize = 0;\r
2482 static char pieceToFontChar[(int) EmptySquare] =\r
2483                               { 'p', 'n', 'b', 'r', 'q', \r
2484                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2485                       'k', 'o', 'm', 'v', 't', 'w', \r
2486                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2487                                                               'l' };\r
2488 \r
2489 extern BOOL SetCharTable( char *table, const char * map );\r
2490 /* [HGM] moved to backend.c */\r
2491 \r
2492 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2493 {\r
2494     HBRUSH hbrush;\r
2495     BYTE r1 = GetRValue( color );\r
2496     BYTE g1 = GetGValue( color );\r
2497     BYTE b1 = GetBValue( color );\r
2498     BYTE r2 = r1 / 2;\r
2499     BYTE g2 = g1 / 2;\r
2500     BYTE b2 = b1 / 2;\r
2501     RECT rc;\r
2502 \r
2503     /* Create a uniform background first */\r
2504     hbrush = CreateSolidBrush( color );\r
2505     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2506     FillRect( hdc, &rc, hbrush );\r
2507     DeleteObject( hbrush );\r
2508     \r
2509     if( mode == 1 ) {\r
2510         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2511         int steps = squareSize / 2;\r
2512         int i;\r
2513 \r
2514         for( i=0; i<steps; i++ ) {\r
2515             BYTE r = r1 - (r1-r2) * i / steps;\r
2516             BYTE g = g1 - (g1-g2) * i / steps;\r
2517             BYTE b = b1 - (b1-b2) * i / steps;\r
2518 \r
2519             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2520             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2521             FillRect( hdc, &rc, hbrush );\r
2522             DeleteObject(hbrush);\r
2523         }\r
2524     }\r
2525     else if( mode == 2 ) {\r
2526         /* Diagonal gradient, good more or less for every piece */\r
2527         POINT triangle[3];\r
2528         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2529         HBRUSH hbrush_old;\r
2530         int steps = squareSize;\r
2531         int i;\r
2532 \r
2533         triangle[0].x = squareSize - steps;\r
2534         triangle[0].y = squareSize;\r
2535         triangle[1].x = squareSize;\r
2536         triangle[1].y = squareSize;\r
2537         triangle[2].x = squareSize;\r
2538         triangle[2].y = squareSize - steps;\r
2539 \r
2540         for( i=0; i<steps; i++ ) {\r
2541             BYTE r = r1 - (r1-r2) * i / steps;\r
2542             BYTE g = g1 - (g1-g2) * i / steps;\r
2543             BYTE b = b1 - (b1-b2) * i / steps;\r
2544 \r
2545             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2546             hbrush_old = SelectObject( hdc, hbrush );\r
2547             Polygon( hdc, triangle, 3 );\r
2548             SelectObject( hdc, hbrush_old );\r
2549             DeleteObject(hbrush);\r
2550             triangle[0].x++;\r
2551             triangle[2].y++;\r
2552         }\r
2553 \r
2554         SelectObject( hdc, hpen );\r
2555     }\r
2556 }\r
2557 \r
2558 /*\r
2559     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2560     seems to work ok. The main problem here is to find the "inside" of a chess\r
2561     piece: follow the steps as explained below.\r
2562 */\r
2563 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2564 {\r
2565     HBITMAP hbm;\r
2566     HBITMAP hbm_old;\r
2567     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2568     RECT rc;\r
2569     SIZE sz;\r
2570     POINT pt;\r
2571     int backColor = whitePieceColor; \r
2572     int foreColor = blackPieceColor;\r
2573     \r
2574     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2575         backColor = appData.fontBackColorWhite;\r
2576         foreColor = appData.fontForeColorWhite;\r
2577     }\r
2578     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2579         backColor = appData.fontBackColorBlack;\r
2580         foreColor = appData.fontForeColorBlack;\r
2581     }\r
2582 \r
2583     /* Mask */\r
2584     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2585 \r
2586     hbm_old = SelectObject( hdc, hbm );\r
2587 \r
2588     rc.left = 0;\r
2589     rc.top = 0;\r
2590     rc.right = squareSize;\r
2591     rc.bottom = squareSize;\r
2592 \r
2593     /* Step 1: background is now black */\r
2594     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2595 \r
2596     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2597 \r
2598     pt.x = (squareSize - sz.cx) / 2;\r
2599     pt.y = (squareSize - sz.cy) / 2;\r
2600 \r
2601     SetBkMode( hdc, TRANSPARENT );\r
2602     SetTextColor( hdc, chroma );\r
2603     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2604     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2605 \r
2606     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2607     /* Step 3: the area outside the piece is filled with white */\r
2608 //    FloodFill( hdc, 0, 0, chroma );\r
2609     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2610     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2611     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2612     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2613     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2614     /* \r
2615         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2616         but if the start point is not inside the piece we're lost!\r
2617         There should be a better way to do this... if we could create a region or path\r
2618         from the fill operation we would be fine for example.\r
2619     */\r
2620 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2621     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2622 \r
2623     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2624         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2625         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2626 \r
2627         SelectObject( dc2, bm2 );\r
2628         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2629         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2630         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2631         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2632         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633 \r
2634         DeleteDC( dc2 );\r
2635         DeleteObject( bm2 );\r
2636     }\r
2637 \r
2638     SetTextColor( hdc, 0 );\r
2639     /* \r
2640         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2641         draw the piece again in black for safety.\r
2642     */\r
2643     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2644 \r
2645     SelectObject( hdc, hbm_old );\r
2646 \r
2647     if( hPieceMask[index] != NULL ) {\r
2648         DeleteObject( hPieceMask[index] );\r
2649     }\r
2650 \r
2651     hPieceMask[index] = hbm;\r
2652 \r
2653     /* Face */\r
2654     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2655 \r
2656     SelectObject( hdc, hbm );\r
2657 \r
2658     {\r
2659         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2660         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2661         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2662 \r
2663         SelectObject( dc1, hPieceMask[index] );\r
2664         SelectObject( dc2, bm2 );\r
2665         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2666         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2667         \r
2668         /* \r
2669             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2670             the piece background and deletes (makes transparent) the rest.\r
2671             Thanks to that mask, we are free to paint the background with the greates\r
2672             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2673             We use this, to make gradients and give the pieces a "roundish" look.\r
2674         */\r
2675         SetPieceBackground( hdc, backColor, 2 );\r
2676         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2677 \r
2678         DeleteDC( dc2 );\r
2679         DeleteDC( dc1 );\r
2680         DeleteObject( bm2 );\r
2681     }\r
2682 \r
2683     SetTextColor( hdc, foreColor );\r
2684     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2685 \r
2686     SelectObject( hdc, hbm_old );\r
2687 \r
2688     if( hPieceFace[index] != NULL ) {\r
2689         DeleteObject( hPieceFace[index] );\r
2690     }\r
2691 \r
2692     hPieceFace[index] = hbm;\r
2693 }\r
2694 \r
2695 static int TranslatePieceToFontPiece( int piece )\r
2696 {\r
2697     switch( piece ) {\r
2698     case BlackPawn:\r
2699         return PM_BP;\r
2700     case BlackKnight:\r
2701         return PM_BN;\r
2702     case BlackBishop:\r
2703         return PM_BB;\r
2704     case BlackRook:\r
2705         return PM_BR;\r
2706     case BlackQueen:\r
2707         return PM_BQ;\r
2708     case BlackKing:\r
2709         return PM_BK;\r
2710     case WhitePawn:\r
2711         return PM_WP;\r
2712     case WhiteKnight:\r
2713         return PM_WN;\r
2714     case WhiteBishop:\r
2715         return PM_WB;\r
2716     case WhiteRook:\r
2717         return PM_WR;\r
2718     case WhiteQueen:\r
2719         return PM_WQ;\r
2720     case WhiteKing:\r
2721         return PM_WK;\r
2722 \r
2723     case BlackAngel:\r
2724         return PM_BA;\r
2725     case BlackMarshall:\r
2726         return PM_BC;\r
2727     case BlackFerz:\r
2728         return PM_BF;\r
2729     case BlackNightrider:\r
2730         return PM_BH;\r
2731     case BlackAlfil:\r
2732         return PM_BE;\r
2733     case BlackWazir:\r
2734         return PM_BW;\r
2735     case BlackUnicorn:\r
2736         return PM_BU;\r
2737     case BlackCannon:\r
2738         return PM_BO;\r
2739     case BlackGrasshopper:\r
2740         return PM_BG;\r
2741     case BlackMan:\r
2742         return PM_BM;\r
2743     case BlackSilver:\r
2744         return PM_BSG;\r
2745     case BlackLance:\r
2746         return PM_BL;\r
2747     case BlackFalcon:\r
2748         return PM_BV;\r
2749     case BlackCobra:\r
2750         return PM_BS;\r
2751     case BlackCardinal:\r
2752         return PM_BAB;\r
2753     case BlackDragon:\r
2754         return PM_BD;\r
2755 \r
2756     case WhiteAngel:\r
2757         return PM_WA;\r
2758     case WhiteMarshall:\r
2759         return PM_WC;\r
2760     case WhiteFerz:\r
2761         return PM_WF;\r
2762     case WhiteNightrider:\r
2763         return PM_WH;\r
2764     case WhiteAlfil:\r
2765         return PM_WE;\r
2766     case WhiteWazir:\r
2767         return PM_WW;\r
2768     case WhiteUnicorn:\r
2769         return PM_WU;\r
2770     case WhiteCannon:\r
2771         return PM_WO;\r
2772     case WhiteGrasshopper:\r
2773         return PM_WG;\r
2774     case WhiteMan:\r
2775         return PM_WM;\r
2776     case WhiteSilver:\r
2777         return PM_WSG;\r
2778     case WhiteLance:\r
2779         return PM_WL;\r
2780     case WhiteFalcon:\r
2781         return PM_WV;\r
2782     case WhiteCobra:\r
2783         return PM_WS;\r
2784     case WhiteCardinal:\r
2785         return PM_WAB;\r
2786     case WhiteDragon:\r
2787         return PM_WD;\r
2788     }\r
2789 \r
2790     return 0;\r
2791 }\r
2792 \r
2793 void CreatePiecesFromFont()\r
2794 {\r
2795     LOGFONT lf;\r
2796     HDC hdc_window = NULL;\r
2797     HDC hdc = NULL;\r
2798     HFONT hfont_old;\r
2799     int fontHeight;\r
2800     int i;\r
2801 \r
2802     if( fontBitmapSquareSize < 0 ) {\r
2803         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2804         return;\r
2805     }\r
2806 \r
2807     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2808         fontBitmapSquareSize = -1;\r
2809         return;\r
2810     }\r
2811 \r
2812     if( fontBitmapSquareSize != squareSize ) {\r
2813         hdc_window = GetDC( hwndMain );\r
2814         hdc = CreateCompatibleDC( hdc_window );\r
2815 \r
2816         if( hPieceFont != NULL ) {\r
2817             DeleteObject( hPieceFont );\r
2818         }\r
2819         else {\r
2820             for( i=0; i<=(int)BlackKing; i++ ) {\r
2821                 hPieceMask[i] = NULL;\r
2822                 hPieceFace[i] = NULL;\r
2823             }\r
2824         }\r
2825 \r
2826         fontHeight = 75;\r
2827 \r
2828         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2829             fontHeight = appData.fontPieceSize;\r
2830         }\r
2831 \r
2832         fontHeight = (fontHeight * squareSize) / 100;\r
2833 \r
2834         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2835         lf.lfWidth = 0;\r
2836         lf.lfEscapement = 0;\r
2837         lf.lfOrientation = 0;\r
2838         lf.lfWeight = FW_NORMAL;\r
2839         lf.lfItalic = 0;\r
2840         lf.lfUnderline = 0;\r
2841         lf.lfStrikeOut = 0;\r
2842         lf.lfCharSet = DEFAULT_CHARSET;\r
2843         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2844         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2845         lf.lfQuality = PROOF_QUALITY;\r
2846         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2847         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2848         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2849 \r
2850         hPieceFont = CreateFontIndirect( &lf );\r
2851 \r
2852         if( hPieceFont == NULL ) {\r
2853             fontBitmapSquareSize = -2;\r
2854         }\r
2855         else {\r
2856             /* Setup font-to-piece character table */\r
2857             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2858                 /* No (or wrong) global settings, try to detect the font */\r
2859                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2860                     /* Alpha */\r
2861                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2862                 }\r
2863                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2864                     /* DiagramTT* family */\r
2865                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2866                 }\r
2867                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2868                     /* Fairy symbols */\r
2869                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2870                 }\r
2871                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2872                     /* Good Companion (Some characters get warped as literal :-( */\r
2873                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2874                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2875                     SetCharTable(pieceToFontChar, s);\r
2876                 }\r
2877                 else {\r
2878                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2879                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2880                 }\r
2881             }\r
2882 \r
2883             /* Create bitmaps */\r
2884             hfont_old = SelectObject( hdc, hPieceFont );\r
2885 #if 0\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2898 \r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2931 #else\r
2932             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2933                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2934                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2935 #endif\r
2936             SelectObject( hdc, hfont_old );\r
2937 \r
2938             fontBitmapSquareSize = squareSize;\r
2939         }\r
2940     }\r
2941 \r
2942     if( hdc != NULL ) {\r
2943         DeleteDC( hdc );\r
2944     }\r
2945 \r
2946     if( hdc_window != NULL ) {\r
2947         ReleaseDC( hwndMain, hdc_window );\r
2948     }\r
2949 }\r
2950 \r
2951 HBITMAP\r
2952 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2953 {\r
2954   char name[128];\r
2955 \r
2956   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2957   if (gameInfo.event &&\r
2958       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2959       strcmp(name, "k80s") == 0) {\r
2960     strcpy(name, "tim");\r
2961   }\r
2962   return LoadBitmap(hinst, name);\r
2963 }\r
2964 \r
2965 \r
2966 /* Insert a color into the program's logical palette\r
2967    structure.  This code assumes the given color is\r
2968    the result of the RGB or PALETTERGB macro, and it\r
2969    knows how those macros work (which is documented).\r
2970 */\r
2971 VOID\r
2972 InsertInPalette(COLORREF color)\r
2973 {\r
2974   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2975 \r
2976   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2977     DisplayFatalError("Too many colors", 0, 1);\r
2978     pLogPal->palNumEntries--;\r
2979     return;\r
2980   }\r
2981 \r
2982   pe->peFlags = (char) 0;\r
2983   pe->peRed = (char) (0xFF & color);\r
2984   pe->peGreen = (char) (0xFF & (color >> 8));\r
2985   pe->peBlue = (char) (0xFF & (color >> 16));\r
2986   return;\r
2987 }\r
2988 \r
2989 \r
2990 VOID\r
2991 InitDrawingColors()\r
2992 {\r
2993   if (pLogPal == NULL) {\r
2994     /* Allocate enough memory for a logical palette with\r
2995      * PALETTESIZE entries and set the size and version fields\r
2996      * of the logical palette structure.\r
2997      */\r
2998     pLogPal = (NPLOGPALETTE)\r
2999       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3000                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3001     pLogPal->palVersion    = 0x300;\r
3002   }\r
3003   pLogPal->palNumEntries = 0;\r
3004 \r
3005   InsertInPalette(lightSquareColor);\r
3006   InsertInPalette(darkSquareColor);\r
3007   InsertInPalette(whitePieceColor);\r
3008   InsertInPalette(blackPieceColor);\r
3009   InsertInPalette(highlightSquareColor);\r
3010   InsertInPalette(premoveHighlightColor);\r
3011 \r
3012   /*  create a logical color palette according the information\r
3013    *  in the LOGPALETTE structure.\r
3014    */\r
3015   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3016 \r
3017   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3018   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3019   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3020   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3021   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3022   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3023   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3024   /* [AS] Force rendering of the font-based pieces */\r
3025   if( fontBitmapSquareSize > 0 ) {\r
3026     fontBitmapSquareSize = 0;\r
3027   }\r
3028 }\r
3029 \r
3030 \r
3031 int\r
3032 BoardWidth(int boardSize, int n)\r
3033 { /* [HGM] argument n added to allow different width and height */\r
3034   int lineGap = sizeInfo[boardSize].lineGap;\r
3035 \r
3036   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3037       lineGap = appData.overrideLineGap;\r
3038   }\r
3039 \r
3040   return (n + 1) * lineGap +\r
3041           n * sizeInfo[boardSize].squareSize;\r
3042 }\r
3043 \r
3044 /* Respond to board resize by dragging edge */\r
3045 VOID\r
3046 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3047 {\r
3048   BoardSize newSize = NUM_SIZES - 1;\r
3049   static int recurse = 0;\r
3050   if (IsIconic(hwndMain)) return;\r
3051   if (recurse > 0) return;\r
3052   recurse++;\r
3053   while (newSize > 0) {\r
3054         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3055         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3056            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3057     newSize--;\r
3058   } \r
3059   boardSize = newSize;\r
3060   InitDrawingSizes(boardSize, flags);\r
3061   recurse--;\r
3062 }\r
3063 \r
3064 \r
3065 \r
3066 VOID\r
3067 InitDrawingSizes(BoardSize boardSize, int flags)\r
3068 {\r
3069   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3070   ChessSquare piece;\r
3071   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3072   HDC hdc;\r
3073   SIZE clockSize, messageSize;\r
3074   HFONT oldFont;\r
3075   char buf[MSG_SIZ];\r
3076   char *str;\r
3077   HMENU hmenu = GetMenu(hwndMain);\r
3078   RECT crect, wrect;\r
3079   int offby;\r
3080   LOGBRUSH logbrush;\r
3081 \r
3082   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3083   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3084 \r
3085   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3086   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3087 \r
3088   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3089   smallLayout = sizeInfo[boardSize].smallLayout;\r
3090   squareSize = sizeInfo[boardSize].squareSize;\r
3091   lineGap = sizeInfo[boardSize].lineGap;\r
3092   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3093 \r
3094   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3095       lineGap = appData.overrideLineGap;\r
3096   }\r
3097 \r
3098   if (tinyLayout != oldTinyLayout) {\r
3099     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3100     if (tinyLayout) {\r
3101       style &= ~WS_SYSMENU;\r
3102       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3103                  "&Minimize\tCtrl+F4");\r
3104     } else {\r
3105       style |= WS_SYSMENU;\r
3106       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3107     }\r
3108     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3109 \r
3110     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3111       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3112         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3113     }\r
3114     DrawMenuBar(hwndMain);\r
3115   }\r
3116 \r
3117   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3118   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3119 \r
3120   /* Get text area sizes */\r
3121   hdc = GetDC(hwndMain);\r
3122   if (appData.clockMode) {\r
3123     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3124   } else {\r
3125     sprintf(buf, "White");\r
3126   }\r
3127   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3128   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3129   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3130   str = "We only care about the height here";\r
3131   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3132   SelectObject(hdc, oldFont);\r
3133   ReleaseDC(hwndMain, hdc);\r
3134 \r
3135   /* Compute where everything goes */\r
3136   if(first.programLogo || second.programLogo) {\r
3137         /* [HGM] logo: if either logo is on, reserve space for it */\r
3138         logoHeight =  2*clockSize.cy;\r
3139         leftLogoRect.left   = OUTER_MARGIN;\r
3140         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3141         leftLogoRect.top    = OUTER_MARGIN;\r
3142         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3143 \r
3144         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3145         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3146         rightLogoRect.top    = OUTER_MARGIN;\r
3147         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3148 \r
3149 \r
3150     blackRect.left = leftLogoRect.right;\r
3151     blackRect.right = rightLogoRect.left;\r
3152     blackRect.top = OUTER_MARGIN;\r
3153     blackRect.bottom = blackRect.top + clockSize.cy;\r
3154 \r
3155     whiteRect.left = blackRect.left ;\r
3156     whiteRect.right = blackRect.right;\r
3157     whiteRect.top = blackRect.bottom;\r
3158     whiteRect.bottom = leftLogoRect.bottom;\r
3159   } else {\r
3160     whiteRect.left = OUTER_MARGIN;\r
3161     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3162     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3163     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3164 \r
3165     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3166     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3167     blackRect.top = whiteRect.top;\r
3168     blackRect.bottom = whiteRect.bottom;\r
3169   }\r
3170 \r
3171   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3172   if (appData.showButtonBar) {\r
3173     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3174       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3175   } else {\r
3176     messageRect.right = OUTER_MARGIN + boardWidth;\r
3177   }\r
3178   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3179   messageRect.bottom = messageRect.top + messageSize.cy;\r
3180 \r
3181   boardRect.left = OUTER_MARGIN;\r
3182   boardRect.right = boardRect.left + boardWidth;\r
3183   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3184   boardRect.bottom = boardRect.top + boardHeight;\r
3185 \r
3186   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3187   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3188   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3189   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3190   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3191     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3192   GetWindowRect(hwndMain, &wrect);\r
3193   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3194                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3195   /* compensate if menu bar wrapped */\r
3196   GetClientRect(hwndMain, &crect);\r
3197   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3198   winHeight += offby;\r
3199   switch (flags) {\r
3200   case WMSZ_TOPLEFT:\r
3201     SetWindowPos(hwndMain, NULL, \r
3202                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3203                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3204     break;\r
3205 \r
3206   case WMSZ_TOPRIGHT:\r
3207   case WMSZ_TOP:\r
3208     SetWindowPos(hwndMain, NULL, \r
3209                  wrect.left, wrect.bottom - winHeight, \r
3210                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3211     break;\r
3212 \r
3213   case WMSZ_BOTTOMLEFT:\r
3214   case WMSZ_LEFT:\r
3215     SetWindowPos(hwndMain, NULL, \r
3216                  wrect.right - winWidth, wrect.top, \r
3217                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3218     break;\r
3219 \r
3220   case WMSZ_BOTTOMRIGHT:\r
3221   case WMSZ_BOTTOM:\r
3222   case WMSZ_RIGHT:\r
3223   default:\r
3224     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3225                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3226     break;\r
3227   }\r
3228 \r
3229   hwndPause = NULL;\r
3230   for (i = 0; i < N_BUTTONS; i++) {\r
3231     if (buttonDesc[i].hwnd != NULL) {\r
3232       DestroyWindow(buttonDesc[i].hwnd);\r
3233       buttonDesc[i].hwnd = NULL;\r
3234     }\r
3235     if (appData.showButtonBar) {\r
3236       buttonDesc[i].hwnd =\r
3237         CreateWindow("BUTTON", buttonDesc[i].label,\r
3238                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3239                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3240                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3241                      (HMENU) buttonDesc[i].id,\r
3242                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3243       if (tinyLayout) {\r
3244         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3245                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3246                     MAKELPARAM(FALSE, 0));\r
3247       }\r
3248       if (buttonDesc[i].id == IDM_Pause)\r
3249         hwndPause = buttonDesc[i].hwnd;\r
3250       buttonDesc[i].wndproc = (WNDPROC)\r
3251         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3252     }\r
3253   }\r
3254   if (gridPen != NULL) DeleteObject(gridPen);\r
3255   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3256   if (premovePen != NULL) DeleteObject(premovePen);\r
3257   if (lineGap != 0) {\r
3258     logbrush.lbStyle = BS_SOLID;\r
3259     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3260     gridPen =\r
3261       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3262                    lineGap, &logbrush, 0, NULL);\r
3263     logbrush.lbColor = highlightSquareColor;\r
3264     highlightPen =\r
3265       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3266                    lineGap, &logbrush, 0, NULL);\r
3267 \r
3268     logbrush.lbColor = premoveHighlightColor; \r
3269     premovePen =\r
3270       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3271                    lineGap, &logbrush, 0, NULL);\r
3272 \r
3273     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3274     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3275       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3276       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3277         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3278       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3279         BOARD_WIDTH * (squareSize + lineGap);\r
3280       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3281     }\r
3282     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3283       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3284       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3285         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3286         lineGap / 2 + (i * (squareSize + lineGap));\r
3287       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3288         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3289       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3290     }\r
3291   }\r
3292 \r
3293   /* [HGM] Licensing requirement */\r
3294 #ifdef GOTHIC\r
3295   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3296 #endif\r
3297 #ifdef FALCON\r
3298   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3299 #endif\r
3300   GothicPopUp( "", VariantNormal);\r
3301 \r
3302 \r
3303 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3304   oldBoardSize = boardSize;\r
3305   oldTinyLayout = tinyLayout;\r
3306 \r
3307   /* Load piece bitmaps for this board size */\r
3308   for (i=0; i<=2; i++) {\r
3309     for (piece = WhitePawn;\r
3310          (int) piece < (int) BlackPawn;\r
3311          piece = (ChessSquare) ((int) piece + 1)) {\r
3312       if (pieceBitmap[i][piece] != NULL)\r
3313         DeleteObject(pieceBitmap[i][piece]);\r
3314     }\r
3315   }\r
3316 \r
3317   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3318   // Orthodox Chess pieces\r
3319   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3320   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3321   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3322   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3323   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3324   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3325   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3326   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3327   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3328   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3329   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3330   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3331   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3332   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3333   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3334   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3335     // in Shogi, Hijack the unused Queen for Lance\r
3336     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3337     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3338     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3339   } else {\r
3340     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3341     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3342     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3343   }\r
3344 \r
3345   if(squareSize <= 72 && squareSize >= 33) { \r
3346     /* A & C are available in most sizes now */\r
3347     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3348       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3349       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3350       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3351       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3352       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3353       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3354       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3355       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3356       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3357       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3358       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3359       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3360     } else { // Smirf-like\r
3361       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3362       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3363       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3364     }\r
3365     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3366       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3367       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3368       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3369     } else { // WinBoard standard\r
3370       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3371       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3372       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3373     }\r
3374   }\r
3375 \r
3376 \r
3377   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3378     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3379     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3380     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3381     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3382     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3383     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3384     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3385     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3386     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3387     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3388     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3389     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3390     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3391     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3392     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3393     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3394     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3395     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3396     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3397     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3398     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3399     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3400     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3401     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3402     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3403     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3404     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3405     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3406     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3407     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3408 \r
3409     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3410       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3411       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3412       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3413       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3414       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3415       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3416       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3417       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3418       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3419       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3420       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3421       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3422     } else {\r
3423       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3424       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3425       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3426       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3427       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3428       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3429       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3430       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3431       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3432       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3433       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3434       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3435     }\r
3436 \r
3437   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3438     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3439     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3440     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3441     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3442     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3443     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3444     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3445     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3446     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3447     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3448     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3449     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3450     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3451     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3452   }\r
3453 \r
3454 \r
3455   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3456   /* special Shogi support in this size */\r
3457   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3458       for (piece = WhitePawn;\r
3459            (int) piece < (int) BlackPawn;\r
3460            piece = (ChessSquare) ((int) piece + 1)) {\r
3461         if (pieceBitmap[i][piece] != NULL)\r
3462           DeleteObject(pieceBitmap[i][piece]);\r
3463       }\r
3464     }\r
3465   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3466   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3467   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3468   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3469   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3470   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3471   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3472   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3473   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3474   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3475   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3476   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3477   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3478   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3479   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3481   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3482   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3483   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3484   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3485   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3486   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3487   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3488   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3489   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3490   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3491   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3492   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3493   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3494   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3495   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3496   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3497   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3498   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3499   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3500   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3501   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3502   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3503   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3504   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3505   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3506   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3507   minorSize = 0;\r
3508   }\r
3509 }\r
3510 \r
3511 HBITMAP\r
3512 PieceBitmap(ChessSquare p, int kind)\r
3513 {\r
3514   if ((int) p >= (int) BlackPawn)\r
3515     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3516 \r
3517   return pieceBitmap[kind][(int) p];\r
3518 }\r
3519 \r
3520 /***************************************************************/\r
3521 \r
3522 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3523 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3524 /*\r
3525 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3526 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3527 */\r
3528 \r
3529 VOID\r
3530 SquareToPos(int row, int column, int * x, int * y)\r
3531 {\r
3532   if (flipView) {\r
3533     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3534     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3535   } else {\r
3536     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3537     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3538   }\r
3539 }\r
3540 \r
3541 VOID\r
3542 DrawCoordsOnDC(HDC hdc)\r
3543 {\r
3544   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
3545   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
3546   char str[2] = { NULLCHAR, NULLCHAR };\r
3547   int oldMode, oldAlign, x, y, start, i;\r
3548   HFONT oldFont;\r
3549   HBRUSH oldBrush;\r
3550 \r
3551   if (!appData.showCoords)\r
3552     return;\r
3553 \r
3554   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3555 \r
3556   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3557   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3558   oldAlign = GetTextAlign(hdc);\r
3559   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3560 \r
3561   y = boardRect.top + lineGap;\r
3562   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3563 \r
3564   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3565   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3566     str[0] = files[start + i];\r
3567     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3568     y += squareSize + lineGap;\r
3569   }\r
3570 \r
3571   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3572 \r
3573   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3574   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3575     str[0] = ranks[start + i];\r
3576     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3577     x += squareSize + lineGap;\r
3578   }    \r
3579 \r
3580   SelectObject(hdc, oldBrush);\r
3581   SetBkMode(hdc, oldMode);\r
3582   SetTextAlign(hdc, oldAlign);\r
3583   SelectObject(hdc, oldFont);\r
3584 }\r
3585 \r
3586 VOID\r
3587 DrawGridOnDC(HDC hdc)\r
3588 {\r
3589   HPEN oldPen;\r
3590  \r
3591   if (lineGap != 0) {\r
3592     oldPen = SelectObject(hdc, gridPen);\r
3593     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3594     SelectObject(hdc, oldPen);\r
3595   }\r
3596 }\r
3597 \r
3598 #define HIGHLIGHT_PEN 0\r
3599 #define PREMOVE_PEN   1\r
3600 \r
3601 VOID\r
3602 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3603 {\r
3604   int x1, y1;\r
3605   HPEN oldPen, hPen;\r
3606   if (lineGap == 0) return;\r
3607   if (flipView) {\r
3608     x1 = boardRect.left +\r
3609       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3610     y1 = boardRect.top +\r
3611       lineGap/2 + y * (squareSize + lineGap);\r
3612   } else {\r
3613     x1 = boardRect.left +\r
3614       lineGap/2 + x * (squareSize + lineGap);\r
3615     y1 = boardRect.top +\r
3616       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3617   }\r
3618   hPen = pen ? premovePen : highlightPen;\r
3619   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3620   MoveToEx(hdc, x1, y1, NULL);\r
3621   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3622   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3623   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3624   LineTo(hdc, x1, y1);\r
3625   SelectObject(hdc, oldPen);\r
3626 }\r
3627 \r
3628 VOID\r
3629 DrawHighlightsOnDC(HDC hdc)\r
3630 {\r
3631   int i;\r
3632   for (i=0; i<2; i++) {\r
3633     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3634       DrawHighlightOnDC(hdc, TRUE,\r
3635                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3636                         HIGHLIGHT_PEN);\r
3637   }\r
3638   for (i=0; i<2; i++) {\r
3639     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3640         premoveHighlightInfo.sq[i].y >= 0) {\r
3641         DrawHighlightOnDC(hdc, TRUE,\r
3642                           premoveHighlightInfo.sq[i].x, \r
3643                           premoveHighlightInfo.sq[i].y,\r
3644                           PREMOVE_PEN);\r
3645     }\r
3646   }\r
3647 }\r
3648 \r
3649 /* Note: sqcolor is used only in monoMode */\r
3650 /* Note that this code is largely duplicated in woptions.c,\r
3651    function DrawSampleSquare, so that needs to be updated too */\r
3652 VOID\r
3653 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3654 {\r
3655   HBITMAP oldBitmap;\r
3656   HBRUSH oldBrush;\r
3657   int tmpSize;\r
3658 \r
3659   if (appData.blindfold) return;\r
3660 \r
3661   /* [AS] Use font-based pieces if needed */\r
3662   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3663     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3664     CreatePiecesFromFont();\r
3665 \r
3666     if( fontBitmapSquareSize == squareSize ) {\r
3667         int index = TranslatePieceToFontPiece(piece);\r
3668 \r
3669         SelectObject( tmphdc, hPieceMask[ index ] );\r
3670 \r
3671         BitBlt( hdc,\r
3672             x, y,\r
3673             squareSize, squareSize,\r
3674             tmphdc,\r
3675             0, 0,\r
3676             SRCAND );\r
3677 \r
3678         SelectObject( tmphdc, hPieceFace[ index ] );\r
3679 \r
3680         BitBlt( hdc,\r
3681             x, y,\r
3682             squareSize, squareSize,\r
3683             tmphdc,\r
3684             0, 0,\r
3685             SRCPAINT );\r
3686 \r
3687         return;\r
3688     }\r
3689   }\r
3690 \r
3691   if (appData.monoMode) {\r
3692     SelectObject(tmphdc, PieceBitmap(piece, \r
3693       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3694     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3695            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3696   } else {\r
3697     tmpSize = squareSize;\r
3698     if(minorSize &&\r
3699         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3700          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3701       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3702       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3703       x += (squareSize - minorSize)>>1;\r
3704       y += squareSize - minorSize - 2;\r
3705       tmpSize = minorSize;\r
3706     }\r
3707     if (color || appData.allWhite ) {\r
3708       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3709       if( color )\r
3710               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3711       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3712       if(appData.upsideDown && color==flipView)\r
3713         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3714       else\r
3715         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3716 #if 0\r
3717       /* Use black piece color for outline of white pieces */\r
3718       /* Not sure this looks really good (though xboard does it).\r
3719          Maybe better to have another selectable color, default black */\r
3720       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3721       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3722       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3723 #else\r
3724       /* Use black for outline of white pieces */\r
3725       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3726       if(appData.upsideDown && color==flipView)\r
3727         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3728       else\r
3729         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3730 #endif\r
3731     } else {\r
3732 #if 0\r
3733       /* Use white piece color for details of black pieces */\r
3734       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3735          WHITE_PIECE ones aren't always the right shape. */\r
3736       /* Not sure this looks really good (though xboard does it).\r
3737          Maybe better to have another selectable color, default medium gray? */\r
3738       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3739       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3740       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3741       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3742       SelectObject(hdc, blackPieceBrush);\r
3743       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744 #else\r
3745       /* Use square color for details of black pieces */\r
3746       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3747       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3748       if(appData.upsideDown && !flipView)\r
3749         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3750       else\r
3751         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3752 #endif\r
3753     }\r
3754     SelectObject(hdc, oldBrush);\r
3755     SelectObject(tmphdc, oldBitmap);\r
3756   }\r
3757 }\r
3758 \r
3759 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3760 int GetBackTextureMode( int algo )\r
3761 {\r
3762     int result = BACK_TEXTURE_MODE_DISABLED;\r
3763 \r
3764     switch( algo ) \r
3765     {\r
3766         case BACK_TEXTURE_MODE_PLAIN:\r
3767             result = 1; /* Always use identity map */\r
3768             break;\r
3769         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3770             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3771             break;\r
3772     }\r
3773 \r
3774     return result;\r
3775 }\r
3776 \r
3777 /* \r
3778     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3779     to handle redraws cleanly (as random numbers would always be different).\r
3780 */\r
3781 VOID RebuildTextureSquareInfo()\r
3782 {\r
3783     BITMAP bi;\r
3784     int lite_w = 0;\r
3785     int lite_h = 0;\r
3786     int dark_w = 0;\r
3787     int dark_h = 0;\r
3788     int row;\r
3789     int col;\r
3790 \r
3791     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3792 \r
3793     if( liteBackTexture != NULL ) {\r
3794         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3795             lite_w = bi.bmWidth;\r
3796             lite_h = bi.bmHeight;\r
3797         }\r
3798     }\r
3799 \r
3800     if( darkBackTexture != NULL ) {\r
3801         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3802             dark_w = bi.bmWidth;\r
3803             dark_h = bi.bmHeight;\r
3804         }\r
3805     }\r
3806 \r
3807     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3808         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3809             if( (col + row) & 1 ) {\r
3810                 /* Lite square */\r
3811                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3812                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3813                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3814                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3815                 }\r
3816             }\r
3817             else {\r
3818                 /* Dark square */\r
3819                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3820                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3821                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3822                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3823                 }\r
3824             }\r
3825         }\r
3826     }\r
3827 }\r
3828 \r
3829 /* [AS] Arrow highlighting support */\r
3830 \r
3831 static int A_WIDTH = 5; /* Width of arrow body */\r
3832 \r
3833 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3834 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3835 \r
3836 static double Sqr( double x )\r
3837 {\r
3838     return x*x;\r
3839 }\r
3840 \r
3841 static int Round( double x )\r
3842 {\r
3843     return (int) (x + 0.5);\r
3844 }\r
3845 \r
3846 /* Draw an arrow between two points using current settings */\r
3847 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3848 {\r
3849     POINT arrow[7];\r
3850     double dx, dy, j, k, x, y;\r
3851 \r
3852     if( d_x == s_x ) {\r
3853         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3854 \r
3855         arrow[0].x = s_x + A_WIDTH;\r
3856         arrow[0].y = s_y;\r
3857 \r
3858         arrow[1].x = s_x + A_WIDTH;\r
3859         arrow[1].y = d_y - h;\r
3860 \r
3861         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3862         arrow[2].y = d_y - h;\r
3863 \r
3864         arrow[3].x = d_x;\r
3865         arrow[3].y = d_y;\r
3866 \r
3867         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3868         arrow[4].y = d_y - h;\r
3869 \r
3870         arrow[5].x = s_x - A_WIDTH;\r
3871         arrow[5].y = d_y - h;\r
3872 \r
3873         arrow[6].x = s_x - A_WIDTH;\r
3874         arrow[6].y = s_y;\r
3875     }\r
3876     else if( d_y == s_y ) {\r
3877         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3878 \r
3879         arrow[0].x = s_x;\r
3880         arrow[0].y = s_y + A_WIDTH;\r
3881 \r
3882         arrow[1].x = d_x - w;\r
3883         arrow[1].y = s_y + A_WIDTH;\r
3884 \r
3885         arrow[2].x = d_x - w;\r
3886         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3887 \r
3888         arrow[3].x = d_x;\r
3889         arrow[3].y = d_y;\r
3890 \r
3891         arrow[4].x = d_x - w;\r
3892         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3893 \r
3894         arrow[5].x = d_x - w;\r
3895         arrow[5].y = s_y - A_WIDTH;\r
3896 \r
3897         arrow[6].x = s_x;\r
3898         arrow[6].y = s_y - A_WIDTH;\r
3899     }\r
3900     else {\r
3901         /* [AS] Needed a lot of paper for this! :-) */\r
3902         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3903         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3904   \r
3905         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3906 \r
3907         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3908 \r
3909         x = s_x;\r
3910         y = s_y;\r
3911 \r
3912         arrow[0].x = Round(x - j);\r
3913         arrow[0].y = Round(y + j*dx);\r
3914 \r
3915         arrow[1].x = Round(x + j);\r
3916         arrow[1].y = Round(y - j*dx);\r
3917 \r
3918         if( d_x > s_x ) {\r
3919             x = (double) d_x - k;\r
3920             y = (double) d_y - k*dy;\r
3921         }\r
3922         else {\r
3923             x = (double) d_x + k;\r
3924             y = (double) d_y + k*dy;\r
3925         }\r
3926 \r
3927         arrow[2].x = Round(x + j);\r
3928         arrow[2].y = Round(y - j*dx);\r
3929 \r
3930         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3931         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3932 \r
3933         arrow[4].x = d_x;\r
3934         arrow[4].y = d_y;\r
3935 \r
3936         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3937         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3938 \r
3939         arrow[6].x = Round(x - j);\r
3940         arrow[6].y = Round(y + j*dx);\r
3941     }\r
3942 \r
3943     Polygon( hdc, arrow, 7 );\r
3944 }\r
3945 \r
3946 /* [AS] Draw an arrow between two squares */\r
3947 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3948 {\r
3949     int s_x, s_y, d_x, d_y;\r
3950     HPEN hpen;\r
3951     HPEN holdpen;\r
3952     HBRUSH hbrush;\r
3953     HBRUSH holdbrush;\r
3954     LOGBRUSH stLB;\r
3955 \r
3956     if( s_col == d_col && s_row == d_row ) {\r
3957         return;\r
3958     }\r
3959 \r
3960     /* Get source and destination points */\r
3961     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3962     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3963 \r
3964     if( d_y > s_y ) {\r
3965         d_y += squareSize / 4;\r
3966     }\r
3967     else if( d_y < s_y ) {\r
3968         d_y += 3 * squareSize / 4;\r
3969     }\r
3970     else {\r
3971         d_y += squareSize / 2;\r
3972     }\r
3973 \r
3974     if( d_x > s_x ) {\r
3975         d_x += squareSize / 4;\r
3976     }\r
3977     else if( d_x < s_x ) {\r
3978         d_x += 3 * squareSize / 4;\r
3979     }\r
3980     else {\r
3981         d_x += squareSize / 2;\r
3982     }\r
3983 \r
3984     s_x += squareSize / 2;\r
3985     s_y += squareSize / 2;\r
3986 \r
3987     /* Adjust width */\r
3988     A_WIDTH = squareSize / 14;\r
3989 \r
3990     /* Draw */\r
3991     stLB.lbStyle = BS_SOLID;\r
3992     stLB.lbColor = appData.highlightArrowColor;\r
3993     stLB.lbHatch = 0;\r
3994 \r
3995     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3996     holdpen = SelectObject( hdc, hpen );\r
3997     hbrush = CreateBrushIndirect( &stLB );\r
3998     holdbrush = SelectObject( hdc, hbrush );\r
3999 \r
4000     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4001 \r
4002     SelectObject( hdc, holdpen );\r
4003     SelectObject( hdc, holdbrush );\r
4004     DeleteObject( hpen );\r
4005     DeleteObject( hbrush );\r
4006 }\r
4007 \r
4008 BOOL HasHighlightInfo()\r
4009 {\r
4010     BOOL result = FALSE;\r
4011 \r
4012     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4013         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4014     {\r
4015         result = TRUE;\r
4016     }\r
4017 \r
4018     return result;\r
4019 }\r
4020 \r
4021 BOOL IsDrawArrowEnabled()\r
4022 {\r
4023     BOOL result = FALSE;\r
4024 \r
4025     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4026         result = TRUE;\r
4027     }\r
4028 \r
4029     return result;\r
4030 }\r
4031 \r
4032 VOID DrawArrowHighlight( HDC hdc )\r
4033 {\r
4034     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4035         DrawArrowBetweenSquares( hdc,\r
4036             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4037             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4038     }\r
4039 }\r
4040 \r
4041 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4042 {\r
4043     HRGN result = NULL;\r
4044 \r
4045     if( HasHighlightInfo() ) {\r
4046         int x1, y1, x2, y2;\r
4047         int sx, sy, dx, dy;\r
4048 \r
4049         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4050         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4051 \r
4052         sx = MIN( x1, x2 );\r
4053         sy = MIN( y1, y2 );\r
4054         dx = MAX( x1, x2 ) + squareSize;\r
4055         dy = MAX( y1, y2 ) + squareSize;\r
4056 \r
4057         result = CreateRectRgn( sx, sy, dx, dy );\r
4058     }\r
4059 \r
4060     return result;\r
4061 }\r
4062 \r
4063 /*\r
4064     Warning: this function modifies the behavior of several other functions. \r
4065     \r
4066     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4067     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4068     repaint is scattered all over the place, which is not good for features such as\r
4069     "arrow highlighting" that require a full repaint of the board.\r
4070 \r
4071     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4072     user interaction, when speed is not so important) but especially to avoid errors\r
4073     in the displayed graphics.\r
4074 \r
4075     In such patched places, I always try refer to this function so there is a single\r
4076     place to maintain knowledge.\r
4077     \r
4078     To restore the original behavior, just return FALSE unconditionally.\r
4079 */\r
4080 BOOL IsFullRepaintPreferrable()\r
4081 {\r
4082     BOOL result = FALSE;\r
4083 \r
4084     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4085         /* Arrow may appear on the board */\r
4086         result = TRUE;\r
4087     }\r
4088 \r
4089     return result;\r
4090 }\r
4091 \r
4092 /* \r
4093     This function is called by DrawPosition to know whether a full repaint must\r
4094     be forced or not.\r
4095 \r
4096     Only DrawPosition may directly call this function, which makes use of \r
4097     some state information. Other function should call DrawPosition specifying \r
4098     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4099 */\r
4100 BOOL DrawPositionNeedsFullRepaint()\r
4101 {\r
4102     BOOL result = FALSE;\r
4103 \r
4104     /* \r
4105         Probably a slightly better policy would be to trigger a full repaint\r
4106         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4107         but animation is fast enough that it's difficult to notice.\r
4108     */\r
4109     if( animInfo.piece == EmptySquare ) {\r
4110         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4111             result = TRUE;\r
4112         }\r
4113     }\r
4114 \r
4115     return result;\r
4116 }\r
4117 \r
4118 VOID\r
4119 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4120 {\r
4121   int row, column, x, y, square_color, piece_color;\r
4122   ChessSquare piece;\r
4123   HBRUSH oldBrush;\r
4124   HDC texture_hdc = NULL;\r
4125 \r
4126   /* [AS] Initialize background textures if needed */\r
4127   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4128       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4129       if( backTextureSquareSize != squareSize \r
4130        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4131           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4132           backTextureSquareSize = squareSize;\r
4133           RebuildTextureSquareInfo();\r
4134       }\r
4135 \r
4136       texture_hdc = CreateCompatibleDC( hdc );\r
4137   }\r
4138 \r
4139   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4140     for (column = 0; column < BOARD_WIDTH; column++) {\r
4141   \r
4142       SquareToPos(row, column, &x, &y);\r
4143 \r
4144       piece = board[row][column];\r
4145 \r
4146       square_color = ((column + row) % 2) == 1;\r
4147       if( gameInfo.variant == VariantXiangqi ) {\r
4148           square_color = !InPalace(row, column);\r
4149           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4150           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4151       }\r
4152       piece_color = (int) piece < (int) BlackPawn;\r
4153 \r
4154 \r
4155       /* [HGM] holdings file: light square or black */\r
4156       if(column == BOARD_LEFT-2) {\r
4157             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4158                 square_color = 1;\r
4159             else {\r
4160                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4161                 continue;\r
4162             }\r
4163       } else\r
4164       if(column == BOARD_RGHT + 1 ) {\r
4165             if( row < gameInfo.holdingsSize )\r
4166                 square_color = 1;\r
4167             else {\r
4168                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4169                 continue;\r
4170             }\r
4171       }\r
4172       if(column == BOARD_LEFT-1 ) /* left align */\r
4173             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4174       else if( column == BOARD_RGHT) /* right align */\r
4175             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4176       else\r
4177       if (appData.monoMode) {\r
4178         if (piece == EmptySquare) {\r
4179           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4180                  square_color ? WHITENESS : BLACKNESS);\r
4181         } else {\r
4182           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4183         }\r
4184       } \r
4185       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4186           /* [AS] Draw the square using a texture bitmap */\r
4187           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4188           int r = row, c = column; // [HGM] do not flip board in flipView\r
4189           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4190 \r
4191           DrawTile( x, y, \r
4192               squareSize, squareSize, \r
4193               hdc, \r
4194               texture_hdc,\r
4195               backTextureSquareInfo[r][c].mode,\r
4196               backTextureSquareInfo[r][c].x,\r
4197               backTextureSquareInfo[r][c].y );\r
4198 \r
4199           SelectObject( texture_hdc, hbm );\r
4200 \r
4201           if (piece != EmptySquare) {\r
4202               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4203           }\r
4204       }\r
4205       else {\r
4206         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4207 \r
4208         oldBrush = SelectObject(hdc, brush );\r
4209         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4210         SelectObject(hdc, oldBrush);\r
4211         if (piece != EmptySquare)\r
4212           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4213       }\r
4214     }\r
4215   }\r
4216 \r
4217   if( texture_hdc != NULL ) {\r
4218     DeleteDC( texture_hdc );\r
4219   }\r
4220 }\r
4221 \r
4222 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4223 void fputDW(FILE *f, int x)\r
4224 {\r
4225         fputc(x     & 255, f);\r
4226         fputc(x>>8  & 255, f);\r
4227         fputc(x>>16 & 255, f);\r
4228         fputc(x>>24 & 255, f);\r
4229 }\r
4230 \r
4231 #define MAX_CLIPS 200   /* more than enough */\r
4232 \r
4233 VOID\r
4234 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4235 {\r
4236 //  HBITMAP bufferBitmap;\r
4237   BITMAP bi;\r
4238 //  RECT Rect;\r
4239   HDC tmphdc;\r
4240   HBITMAP hbm;\r
4241   int w = 100, h = 50;\r
4242 \r
4243   if(cps->programLogo == NULL) return;\r
4244 //  GetClientRect(hwndMain, &Rect);\r
4245 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4246 //                                      Rect.bottom-Rect.top+1);\r
4247   tmphdc = CreateCompatibleDC(hdc);\r
4248   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4249   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4250             w = bi.bmWidth;\r
4251             h = bi.bmHeight;\r
4252   }\r
4253   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4254                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4255   SelectObject(tmphdc, hbm);\r
4256   DeleteDC(tmphdc);\r
4257 }\r
4258 \r
4259 VOID\r
4260 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4261 {\r
4262   static Board lastReq, lastDrawn;\r
4263   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4264   static int lastDrawnFlipView = 0;\r
4265   static int lastReqValid = 0, lastDrawnValid = 0;\r
4266   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4267   HDC tmphdc;\r
4268   HDC hdcmem;\r
4269   HBITMAP bufferBitmap;\r
4270   HBITMAP oldBitmap;\r
4271   RECT Rect;\r
4272   HRGN clips[MAX_CLIPS];\r
4273   ChessSquare dragged_piece = EmptySquare;\r
4274 \r
4275   /* I'm undecided on this - this function figures out whether a full\r
4276    * repaint is necessary on its own, so there's no real reason to have the\r
4277    * caller tell it that.  I think this can safely be set to FALSE - but\r
4278    * if we trust the callers not to request full repaints unnessesarily, then\r
4279    * we could skip some clipping work.  In other words, only request a full\r
4280    * redraw when the majority of pieces have changed positions (ie. flip, \r
4281    * gamestart and similar)  --Hawk\r
4282    */\r
4283   Boolean fullrepaint = repaint;\r
4284 \r
4285   if( DrawPositionNeedsFullRepaint() ) {\r
4286       fullrepaint = TRUE;\r
4287   }\r
4288 \r
4289 #if 0\r
4290   if( fullrepaint ) {\r
4291       static int repaint_count = 0;\r
4292       char buf[128];\r
4293 \r
4294       repaint_count++;\r
4295       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4296       OutputDebugString( buf );\r
4297   }\r
4298 #endif\r
4299 \r
4300   if (board == NULL) {\r
4301     if (!lastReqValid) {\r
4302       return;\r
4303     }\r
4304     board = lastReq;\r
4305   } else {\r
4306     CopyBoard(lastReq, board);\r
4307     lastReqValid = 1;\r
4308   }\r
4309 \r
4310   if (doingSizing) {\r
4311     return;\r
4312   }\r
4313 \r
4314   if (IsIconic(hwndMain)) {\r
4315     return;\r
4316   }\r
4317 \r
4318   if (hdc == NULL) {\r
4319     hdc = GetDC(hwndMain);\r
4320     if (!appData.monoMode) {\r
4321       SelectPalette(hdc, hPal, FALSE);\r
4322       RealizePalette(hdc);\r
4323     }\r
4324     releaseDC = TRUE;\r
4325   } else {\r
4326     releaseDC = FALSE;\r
4327   }\r
4328 \r
4329 #if 0\r
4330   fprintf(debugFP, "*******************************\n"\r
4331                    "repaint = %s\n"\r
4332                    "dragInfo.from (%d,%d)\n"\r
4333                    "dragInfo.start (%d,%d)\n"\r
4334                    "dragInfo.pos (%d,%d)\n"\r
4335                    "dragInfo.lastpos (%d,%d)\n", \r
4336                     repaint ? "TRUE" : "FALSE",\r
4337                     dragInfo.from.x, dragInfo.from.y, \r
4338                     dragInfo.start.x, dragInfo.start.y,\r
4339                     dragInfo.pos.x, dragInfo.pos.y,\r
4340                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4341   fprintf(debugFP, "prev:  ");\r
4342   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4343     for (column = 0; column < BOARD_WIDTH; column++) {\r
4344       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4345     }\r
4346   }\r
4347   fprintf(debugFP, "\n");\r
4348   fprintf(debugFP, "board: ");\r
4349   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4350     for (column = 0; column < BOARD_WIDTH; column++) {\r
4351       fprintf(debugFP, "%d ", board[row][column]);\r
4352     }\r
4353   }\r
4354   fprintf(debugFP, "\n");\r
4355   fflush(debugFP);\r
4356 #endif\r
4357 \r
4358   /* Create some work-DCs */\r
4359   hdcmem = CreateCompatibleDC(hdc);\r
4360   tmphdc = CreateCompatibleDC(hdc);\r
4361 \r
4362   /* If dragging is in progress, we temporarely remove the piece */\r
4363   /* [HGM] or temporarily decrease count if stacked              */\r
4364   /*       !! Moved to before board compare !!                   */\r
4365   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4366     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4367     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4368             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4369         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4370     } else \r
4371     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4372             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4373         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4374     } else \r
4375         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4376   }\r
4377 \r
4378   /* Figure out which squares need updating by comparing the \r
4379    * newest board with the last drawn board and checking if\r
4380    * flipping has changed.\r
4381    */\r
4382   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4383     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4384       for (column = 0; column < BOARD_WIDTH; column++) {\r
4385         if (lastDrawn[row][column] != board[row][column]) {\r
4386           SquareToPos(row, column, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4389         }\r
4390       }\r
4391     }\r
4392     for (i=0; i<2; i++) {\r
4393       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4394           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4395         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4396             lastDrawnHighlight.sq[i].y >= 0) {\r
4397           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4398                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4399           clips[num_clips++] =\r
4400             CreateRectRgn(x - lineGap, y - lineGap, \r
4401                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4402         }\r
4403         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4404           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4405           clips[num_clips++] =\r
4406             CreateRectRgn(x - lineGap, y - lineGap, \r
4407                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4408         }\r
4409       }\r
4410     }\r
4411     for (i=0; i<2; i++) {\r
4412       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4413           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4414         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4415             lastDrawnPremove.sq[i].y >= 0) {\r
4416           SquareToPos(lastDrawnPremove.sq[i].y,\r
4417                       lastDrawnPremove.sq[i].x, &x, &y);\r
4418           clips[num_clips++] =\r
4419             CreateRectRgn(x - lineGap, y - lineGap, \r
4420                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4421         }\r
4422         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4423             premoveHighlightInfo.sq[i].y >= 0) {\r
4424           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4425                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4426           clips[num_clips++] =\r
4427             CreateRectRgn(x - lineGap, y - lineGap, \r
4428                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4429         }\r
4430       }\r
4431     }\r
4432   } else {\r
4433     fullrepaint = TRUE;\r
4434   }\r
4435 \r
4436   /* Create a buffer bitmap - this is the actual bitmap\r
4437    * being written to.  When all the work is done, we can\r
4438    * copy it to the real DC (the screen).  This avoids\r
4439    * the problems with flickering.\r
4440    */\r
4441   GetClientRect(hwndMain, &Rect);\r
4442   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4443                                         Rect.bottom-Rect.top+1);\r
4444   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4445   if (!appData.monoMode) {\r
4446     SelectPalette(hdcmem, hPal, FALSE);\r
4447   }\r
4448 \r
4449   /* Create clips for dragging */\r
4450   if (!fullrepaint) {\r
4451     if (dragInfo.from.x >= 0) {\r
4452       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4453       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4454     }\r
4455     if (dragInfo.start.x >= 0) {\r
4456       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4457       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4458     }\r
4459     if (dragInfo.pos.x >= 0) {\r
4460       x = dragInfo.pos.x - squareSize / 2;\r
4461       y = dragInfo.pos.y - squareSize / 2;\r
4462       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4463     }\r
4464     if (dragInfo.lastpos.x >= 0) {\r
4465       x = dragInfo.lastpos.x - squareSize / 2;\r
4466       y = dragInfo.lastpos.y - squareSize / 2;\r
4467       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4468     }\r
4469   }\r
4470 \r
4471   /* Are we animating a move?  \r
4472    * If so, \r
4473    *   - remove the piece from the board (temporarely)\r
4474    *   - calculate the clipping region\r
4475    */\r
4476   if (!fullrepaint) {\r
4477     if (animInfo.piece != EmptySquare) {\r
4478       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4479       x = boardRect.left + animInfo.lastpos.x;\r
4480       y = boardRect.top + animInfo.lastpos.y;\r
4481       x2 = boardRect.left + animInfo.pos.x;\r
4482       y2 = boardRect.top + animInfo.pos.y;\r
4483       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4484       /* Slight kludge.  The real problem is that after AnimateMove is\r
4485          done, the position on the screen does not match lastDrawn.\r
4486          This currently causes trouble only on e.p. captures in\r
4487          atomic, where the piece moves to an empty square and then\r
4488          explodes.  The old and new positions both had an empty square\r
4489          at the destination, but animation has drawn a piece there and\r
4490          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4491       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4492     }\r
4493   }\r
4494 \r
4495   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4496   if (num_clips == 0)\r
4497     fullrepaint = TRUE;\r
4498 \r
4499   /* Set clipping on the memory DC */\r
4500   if (!fullrepaint) {\r
4501     SelectClipRgn(hdcmem, clips[0]);\r
4502     for (x = 1; x < num_clips; x++) {\r
4503       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4504         abort();  // this should never ever happen!\r
4505     }\r
4506   }\r
4507 \r
4508   /* Do all the drawing to the memory DC */\r
4509   if(explodeInfo.radius) { // [HGM] atomic\r
4510         HBRUSH oldBrush;\r
4511         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4512         SquareToPos(explodeInfo.y, explodeInfo.x, &x, &y);\r
4513         x += squareSize/2;\r
4514         y += squareSize/2;\r
4515         if(!fullrepaint) {\r
4516           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4517           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4518         }\r
4519         DrawGridOnDC(hdcmem);\r
4520         DrawHighlightsOnDC(hdcmem);\r
4521         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4522         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4523         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4524         SelectObject(hdcmem, oldBrush);\r
4525   } else {\r
4526     DrawGridOnDC(hdcmem);\r
4527     DrawHighlightsOnDC(hdcmem);\r
4528     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4529   }\r
4530   if(logoHeight) {\r
4531         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4532         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4533   }\r
4534 \r
4535   if( appData.highlightMoveWithArrow ) {\r
4536     DrawArrowHighlight(hdcmem);\r
4537   }\r
4538 \r
4539   DrawCoordsOnDC(hdcmem);\r
4540 \r
4541   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4542                  /* to make sure lastDrawn contains what is actually drawn */\r
4543 \r
4544   /* Put the dragged piece back into place and draw it (out of place!) */\r
4545     if (dragged_piece != EmptySquare) {\r
4546     /* [HGM] or restack */\r
4547     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4548                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4549     else\r
4550     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4551                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4552     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4553     x = dragInfo.pos.x - squareSize / 2;\r
4554     y = dragInfo.pos.y - squareSize / 2;\r
4555     DrawPieceOnDC(hdcmem, dragged_piece,\r
4556                   ((int) dragged_piece < (int) BlackPawn), \r
4557                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4558   }   \r
4559   \r
4560   /* Put the animated piece back into place and draw it */\r
4561   if (animInfo.piece != EmptySquare) {\r
4562     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4563     x = boardRect.left + animInfo.pos.x;\r
4564     y = boardRect.top + animInfo.pos.y;\r
4565     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4566                   ((int) animInfo.piece < (int) BlackPawn),\r
4567                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4568   }\r
4569 \r
4570   /* Release the bufferBitmap by selecting in the old bitmap \r
4571    * and delete the memory DC\r
4572    */\r
4573   SelectObject(hdcmem, oldBitmap);\r
4574   DeleteDC(hdcmem);\r
4575 \r
4576   /* Set clipping on the target DC */\r
4577   if (!fullrepaint) {\r
4578     SelectClipRgn(hdc, clips[0]);\r
4579     for (x = 1; x < num_clips; x++) {\r
4580       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4581         abort();   // this should never ever happen!\r
4582     } \r
4583   }\r
4584 \r
4585   /* Copy the new bitmap onto the screen in one go.\r
4586    * This way we avoid any flickering\r
4587    */\r
4588   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4589   BitBlt(hdc, boardRect.left, boardRect.top,\r
4590          boardRect.right - boardRect.left,\r
4591          boardRect.bottom - boardRect.top,\r
4592          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4593   if(saveDiagFlag) { \r
4594     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4595     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4596 \r
4597     GetObject(bufferBitmap, sizeof(b), &b);\r
4598     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4599         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4600         bih.biWidth = b.bmWidth;\r
4601         bih.biHeight = b.bmHeight;\r
4602         bih.biPlanes = 1;\r
4603         bih.biBitCount = b.bmBitsPixel;\r
4604         bih.biCompression = 0;\r
4605         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4606         bih.biXPelsPerMeter = 0;\r
4607         bih.biYPelsPerMeter = 0;\r
4608         bih.biClrUsed = 0;\r
4609         bih.biClrImportant = 0;\r
4610 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4611 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4612         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4613 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4614 \r
4615 #if 1\r
4616         wb = b.bmWidthBytes;\r
4617         // count colors\r
4618         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4619                 int k = ((int*) pData)[i];\r
4620                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4621                 if(j >= 16) break;\r
4622                 color[j] = k;\r
4623                 if(j >= nrColors) nrColors = j+1;\r
4624         }\r
4625         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4626                 INT p = 0;\r
4627                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4628                     for(w=0; w<(wb>>2); w+=2) {\r
4629                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4630                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4631                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4632                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4633                         pData[p++] = m | j<<4;\r
4634                     }\r
4635                     while(p&3) pData[p++] = 0;\r
4636                 }\r
4637                 fac = 3;\r
4638                 wb = ((wb+31)>>5)<<2;\r
4639         }\r
4640         // write BITMAPFILEHEADER\r
4641         fprintf(diagFile, "BM");\r
4642         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4643         fputDW(diagFile, 0);\r
4644         fputDW(diagFile, 0x36 + (fac?64:0));\r
4645         // write BITMAPINFOHEADER\r
4646         fputDW(diagFile, 40);\r
4647         fputDW(diagFile, b.bmWidth);\r
4648         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4649         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4650         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4651         fputDW(diagFile, 0);\r
4652         fputDW(diagFile, 0);\r
4653         fputDW(diagFile, 0);\r
4654         fputDW(diagFile, 0);\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         // write color table\r
4658         if(fac)\r
4659         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4660         // write bitmap data\r
4661         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4662                 fputc(pData[i], diagFile);\r
4663 #endif\r
4664      }\r
4665   }\r
4666 \r
4667   SelectObject(tmphdc, oldBitmap);\r
4668 \r
4669   /* Massive cleanup */\r
4670   for (x = 0; x < num_clips; x++)\r
4671     DeleteObject(clips[x]);\r
4672 \r
4673   DeleteDC(tmphdc);\r
4674   DeleteObject(bufferBitmap);\r
4675 \r
4676   if (releaseDC) \r
4677     ReleaseDC(hwndMain, hdc);\r
4678   \r
4679   if (lastDrawnFlipView != flipView) {\r
4680     if (flipView)\r
4681       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4682     else\r
4683       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4684   }\r
4685 \r
4686 /*  CopyBoard(lastDrawn, board);*/\r
4687   lastDrawnHighlight = highlightInfo;\r
4688   lastDrawnPremove   = premoveHighlightInfo;\r
4689   lastDrawnFlipView = flipView;\r
4690   lastDrawnValid = 1;\r
4691 }\r
4692 \r
4693 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4694 int\r
4695 SaveDiagram(f)\r
4696      FILE *f;\r
4697 {\r
4698     saveDiagFlag = 1; diagFile = f;\r
4699     HDCDrawPosition(NULL, TRUE, NULL);\r
4700 \r
4701     saveDiagFlag = 0;\r
4702 \r
4703 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4704     \r
4705     fclose(f);\r
4706     return TRUE;\r
4707 }\r
4708 \r
4709 \r
4710 /*---------------------------------------------------------------------------*\\r
4711 | CLIENT PAINT PROCEDURE\r
4712 |   This is the main event-handler for the WM_PAINT message.\r
4713 |\r
4714 \*---------------------------------------------------------------------------*/\r
4715 VOID\r
4716 PaintProc(HWND hwnd)\r
4717 {\r
4718   HDC         hdc;\r
4719   PAINTSTRUCT ps;\r
4720   HFONT       oldFont;\r
4721 \r
4722   if((hdc = BeginPaint(hwnd, &ps))) {\r
4723     if (IsIconic(hwnd)) {\r
4724       DrawIcon(hdc, 2, 2, iconCurrent);\r
4725     } else {\r
4726       if (!appData.monoMode) {\r
4727         SelectPalette(hdc, hPal, FALSE);\r
4728         RealizePalette(hdc);\r
4729       }\r
4730       HDCDrawPosition(hdc, 1, NULL);\r
4731       oldFont =\r
4732         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4733       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4734                  ETO_CLIPPED|ETO_OPAQUE,\r
4735                  &messageRect, messageText, strlen(messageText), NULL);\r
4736       SelectObject(hdc, oldFont);\r
4737       DisplayBothClocks();\r
4738     }\r
4739     EndPaint(hwnd,&ps);\r
4740   }\r
4741 \r
4742   return;\r
4743 }\r
4744 \r
4745 \r
4746 /*\r
4747  * If the user selects on a border boundary, return -1; if off the board,\r
4748  *   return -2.  Otherwise map the event coordinate to the square.\r
4749  * The offset boardRect.left or boardRect.top must already have been\r
4750  *   subtracted from x.\r
4751  */\r
4752 int\r
4753 EventToSquare(int x)\r
4754 {\r
4755   if (x <= 0)\r
4756     return -2;\r
4757   if (x < lineGap)\r
4758     return -1;\r
4759   x -= lineGap;\r
4760   if ((x % (squareSize + lineGap)) >= squareSize)\r
4761     return -1;\r
4762   x /= (squareSize + lineGap);\r
4763   if (x >= BOARD_SIZE)\r
4764     return -2;\r
4765   return x;\r
4766 }\r
4767 \r
4768 typedef struct {\r
4769   char piece;\r
4770   int command;\r
4771   char* name;\r
4772 } DropEnable;\r
4773 \r
4774 DropEnable dropEnables[] = {\r
4775   { 'P', DP_Pawn, "Pawn" },\r
4776   { 'N', DP_Knight, "Knight" },\r
4777   { 'B', DP_Bishop, "Bishop" },\r
4778   { 'R', DP_Rook, "Rook" },\r
4779   { 'Q', DP_Queen, "Queen" },\r
4780 };\r
4781 \r
4782 VOID\r
4783 SetupDropMenu(HMENU hmenu)\r
4784 {\r
4785   int i, count, enable;\r
4786   char *p;\r
4787   extern char white_holding[], black_holding[];\r
4788   char item[MSG_SIZ];\r
4789 \r
4790   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4791     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4792                dropEnables[i].piece);\r
4793     count = 0;\r
4794     while (p && *p++ == dropEnables[i].piece) count++;\r
4795     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4796     enable = count > 0 || !appData.testLegality\r
4797       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4798                       && !appData.icsActive);\r
4799     ModifyMenu(hmenu, dropEnables[i].command,\r
4800                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4801                dropEnables[i].command, item);\r
4802   }\r
4803 }\r
4804 \r
4805 static int fromX = -1, fromY = -1, toX, toY;\r
4806 \r
4807 /* Event handler for mouse messages */\r
4808 VOID\r
4809 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4810 {\r
4811   int x, y;\r
4812   POINT pt;\r
4813   static int recursive = 0;\r
4814   HMENU hmenu;\r
4815 //  BOOLEAN needsRedraw = FALSE;\r
4816   BOOLEAN saveAnimate;\r
4817   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4818   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4819   ChessMove moveType;\r
4820 \r
4821   if (recursive) {\r
4822     if (message == WM_MBUTTONUP) {\r
4823       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4824          to the middle button: we simulate pressing the left button too!\r
4825          */\r
4826       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4827       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4828     }\r
4829     return;\r
4830   }\r
4831   recursive++;\r
4832   \r
4833   pt.x = LOWORD(lParam);\r
4834   pt.y = HIWORD(lParam);\r
4835   x = EventToSquare(pt.x - boardRect.left);\r
4836   y = EventToSquare(pt.y - boardRect.top);\r
4837   if (!flipView && y >= 0) {\r
4838     y = BOARD_HEIGHT - 1 - y;\r
4839   }\r
4840   if (flipView && x >= 0) {\r
4841     x = BOARD_WIDTH - 1 - x;\r
4842   }\r
4843 \r
4844   switch (message) {\r
4845   case WM_LBUTTONDOWN:\r
4846     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4847         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4848         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4849         if(gameInfo.holdingsWidth && \r
4850                 (WhiteOnMove(currentMove) \r
4851                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4852                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4853             // click in right holdings, for determining promotion piece\r
4854             ChessSquare p = boards[currentMove][y][x];\r
4855             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4856             if(p != EmptySquare) {\r
4857                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4858                 fromX = fromY = -1;\r
4859                 break;\r
4860             }\r
4861         }\r
4862         DrawPosition(FALSE, boards[currentMove]);\r
4863         break;\r
4864     }\r
4865     ErrorPopDown();\r
4866     sameAgain = FALSE;\r
4867     if (y == -2) {\r
4868       /* Downclick vertically off board; check if on clock */\r
4869       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4870         if (gameMode == EditPosition) {\r
4871           SetWhiteToPlayEvent();\r
4872         } else if (gameMode == IcsPlayingBlack ||\r
4873                    gameMode == MachinePlaysWhite) {\r
4874           CallFlagEvent();\r
4875         } else if (gameMode == EditGame) {\r
4876           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4877         }\r
4878       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4879         if (gameMode == EditPosition) {\r
4880           SetBlackToPlayEvent();\r
4881         } else if (gameMode == IcsPlayingWhite ||\r
4882                    gameMode == MachinePlaysBlack) {\r
4883           CallFlagEvent();\r
4884         } else if (gameMode == EditGame) {\r
4885           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4886         }\r
4887       }\r
4888       if (!appData.highlightLastMove) {\r
4889         ClearHighlights();\r
4890         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4891       }\r
4892       fromX = fromY = -1;\r
4893       dragInfo.start.x = dragInfo.start.y = -1;\r
4894       dragInfo.from = dragInfo.start;\r
4895       break;\r
4896     } else if (x < 0 || y < 0\r
4897       /* [HGM] block clicks between board and holdings */\r
4898               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4899               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4900               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4901         /* EditPosition, empty square, or different color piece;\r
4902            click-click move is possible */\r
4903                                ) {\r
4904       break;\r
4905     } else if (fromX == x && fromY == y) {\r
4906       /* Downclick on same square again */\r
4907       ClearHighlights();\r
4908       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4909       sameAgain = TRUE;  \r
4910     } else if (fromX != -1 &&\r
4911                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4912                                                                         ) {\r
4913       /* Downclick on different square. */\r
4914       /* [HGM] if on holdings file, should count as new first click ! */\r
4915       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4916         toX = x;\r
4917         toY = y;\r
4918         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4919            to make sure move is legal before showing promotion popup */\r
4920         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4921         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4922                 fromX = fromY = -1; \r
4923                 ClearHighlights();\r
4924                 DrawPosition(FALSE, boards[currentMove]);\r
4925                 break; \r
4926         } else \r
4927         if(moveType != ImpossibleMove) {\r
4928           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4929           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4930             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4931               appData.alwaysPromoteToQueen)) {\r
4932                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4933                   if (!appData.highlightLastMove) {\r
4934                       ClearHighlights();\r
4935                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4936                   }\r
4937           } else\r
4938           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4939                   SetHighlights(fromX, fromY, toX, toY);\r
4940                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4941                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4942                      If promotion to Q is legal, all are legal! */\r
4943                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4944                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4945                     // kludge to temporarily execute move on display, wthout promotng yet\r
4946                     promotionChoice = TRUE;\r
4947                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4948                     boards[currentMove][toY][toX] = p;\r
4949                     DrawPosition(FALSE, boards[currentMove]);\r
4950                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4951                     boards[currentMove][toY][toX] = q;\r
4952                   } else\r
4953                   PromotionPopup(hwnd);\r
4954           } else {       /* not a promotion */\r
4955              if (appData.animate || appData.highlightLastMove) {\r
4956                  SetHighlights(fromX, fromY, toX, toY);\r
4957              } else {\r
4958                  ClearHighlights();\r
4959              }\r
4960              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4961              fromX = fromY = -1;\r
4962              if (appData.animate && !appData.highlightLastMove) {\r
4963                   ClearHighlights();\r
4964                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4965              }\r
4966           }\r
4967           break;\r
4968         }\r
4969         if (gotPremove) {\r
4970             /* [HGM] it seemed that braces were missing here */\r
4971             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4972             fromX = fromY = -1;\r
4973             break;\r
4974         }\r
4975       }\r
4976       ClearHighlights();\r
4977       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4978     }\r
4979     /* First downclick, or restart on a square with same color piece */\r
4980     if (!frozen && OKToStartUserMove(x, y)) {\r
4981       fromX = x;\r
4982       fromY = y;\r
4983       dragInfo.lastpos = pt;\r
4984       dragInfo.from.x = fromX;\r
4985       dragInfo.from.y = fromY;\r
4986       dragInfo.start = dragInfo.from;\r
4987       SetCapture(hwndMain);\r
4988     } else {\r
4989       fromX = fromY = -1;\r
4990       dragInfo.start.x = dragInfo.start.y = -1;\r
4991       dragInfo.from = dragInfo.start;\r
4992       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4993     }\r
4994     break;\r
4995 \r
4996   case WM_LBUTTONUP:\r
4997     ReleaseCapture();\r
4998     if (fromX == -1) break;\r
4999     if (x == fromX && y == fromY) {\r
5000       dragInfo.from.x = dragInfo.from.y = -1;\r
5001       /* Upclick on same square */\r
5002       if (sameAgain) {\r
5003         /* Clicked same square twice: abort click-click move */\r
5004         fromX = fromY = -1;\r
5005         gotPremove = 0;\r
5006         ClearPremoveHighlights();\r
5007       } else {\r
5008         /* First square clicked: start click-click move */\r
5009         SetHighlights(fromX, fromY, -1, -1);\r
5010       }\r
5011       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5012     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5013       /* Errant click; ignore */\r
5014       break;\r
5015     } else {\r
5016       /* Finish drag move. */\r
5017     if (appData.debugMode) {\r
5018         fprintf(debugFP, "release\n");\r
5019     }\r
5020       dragInfo.from.x = dragInfo.from.y = -1;\r
5021       toX = x;\r
5022       toY = y;\r
5023       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5024       appData.animate = appData.animate && !appData.animateDragging;\r
5025       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5026       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5027                 fromX = fromY = -1; \r
5028                 ClearHighlights();\r
5029                 DrawPosition(FALSE, boards[currentMove]);\r
5030                 break; \r
5031       } else \r
5032       if(moveType != ImpossibleMove) {\r
5033           /* [HGM] use move type to determine if move is promotion.\r
5034              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5035           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5036             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5037               appData.alwaysPromoteToQueen)) \r
5038                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5039           else \r
5040           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5041                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5042                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5043                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5044                     // kludge to temporarily execute move on display, wthout promotng yet\r
5045                     promotionChoice = TRUE;\r
5046                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5047                     boards[currentMove][toY][toX] = p;\r
5048                     DrawPosition(FALSE, boards[currentMove]);\r
5049                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5050                     boards[currentMove][toY][toX] = q;\r
5051                     break;\r
5052                   } else\r
5053                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5054           } else {\r
5055             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5056                         && boards[currentMove][toY][toX] != EmptySquare) AnimateAtomicCapture(toX, toY, 20);\r
5057             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5058           }\r
5059       }\r
5060       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061       appData.animate = saveAnimate;\r
5062       fromX = fromY = -1;\r
5063       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5064         ClearHighlights();\r
5065       }\r
5066       if (appData.animate || appData.animateDragging ||\r
5067           appData.highlightDragging || gotPremove) {\r
5068         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5069       }\r
5070     }\r
5071     dragInfo.start.x = dragInfo.start.y = -1; \r
5072     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5073     break;\r
5074 \r
5075   case WM_MOUSEMOVE:\r
5076     if ((appData.animateDragging || appData.highlightDragging)\r
5077         && (wParam & MK_LBUTTON)\r
5078         && dragInfo.from.x >= 0) \r
5079     {\r
5080       BOOL full_repaint = FALSE;\r
5081 \r
5082       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5083       if (appData.animateDragging) {\r
5084         dragInfo.pos = pt;\r
5085       }\r
5086       if (appData.highlightDragging) {\r
5087         SetHighlights(fromX, fromY, x, y);\r
5088         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5089             full_repaint = TRUE;\r
5090         }\r
5091       }\r
5092       \r
5093       DrawPosition( full_repaint, NULL);\r
5094       \r
5095       dragInfo.lastpos = dragInfo.pos;\r
5096     }\r
5097     break;\r
5098 \r
5099   case WM_MOUSEWHEEL: // [DM]\r
5100     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5101        /* Mouse Wheel is being rolled forward\r
5102         * Play moves forward\r
5103         */\r
5104        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5105                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5106        /* Mouse Wheel is being rolled backward\r
5107         * Play moves backward\r
5108         */\r
5109        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5110                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5111     }\r
5112     break;\r
5113 \r
5114   case WM_MBUTTONDOWN:\r
5115   case WM_RBUTTONDOWN:\r
5116     ErrorPopDown();\r
5117     ReleaseCapture();\r
5118     fromX = fromY = -1;\r
5119     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5120     dragInfo.start.x = dragInfo.start.y = -1;\r
5121     dragInfo.from = dragInfo.start;\r
5122     dragInfo.lastpos = dragInfo.pos;\r
5123     if (appData.highlightDragging) {\r
5124       ClearHighlights();\r
5125     }\r
5126     if(y == -2) {\r
5127       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5128       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5129           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5130       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5131           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5132       }\r
5133     }\r
5134     DrawPosition(TRUE, NULL);\r
5135 \r
5136     switch (gameMode) {\r
5137     case EditPosition:\r
5138     case IcsExamining:\r
5139       if (x < 0 || y < 0) break;\r
5140       fromX = x;\r
5141       fromY = y;\r
5142       if (message == WM_MBUTTONDOWN) {\r
5143         buttonCount = 3;  /* even if system didn't think so */\r
5144         if (wParam & MK_SHIFT) \r
5145           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5146         else\r
5147           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5148       } else { /* message == WM_RBUTTONDOWN */\r
5149 #if 0\r
5150         if (buttonCount == 3) {\r
5151           if (wParam & MK_SHIFT) \r
5152             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5153           else\r
5154             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5155         } else {\r
5156           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5157         }\r
5158 #else\r
5159         /* Just have one menu, on the right button.  Windows users don't\r
5160            think to try the middle one, and sometimes other software steals\r
5161            it, or it doesn't really exist. */\r
5162         if(gameInfo.variant != VariantShogi)\r
5163             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5164         else\r
5165             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5166 #endif\r
5167       }\r
5168       break;\r
5169     case IcsPlayingWhite:\r
5170     case IcsPlayingBlack:\r
5171     case EditGame:\r
5172     case MachinePlaysWhite:\r
5173     case MachinePlaysBlack:\r
5174       if (appData.testLegality &&\r
5175           gameInfo.variant != VariantBughouse &&\r
5176           gameInfo.variant != VariantCrazyhouse) break;\r
5177       if (x < 0 || y < 0) break;\r
5178       fromX = x;\r
5179       fromY = y;\r
5180       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5181       SetupDropMenu(hmenu);\r
5182       MenuPopup(hwnd, pt, hmenu, -1);\r
5183       break;\r
5184     default:\r
5185       break;\r
5186     }\r
5187     break;\r
5188   }\r
5189 \r
5190   recursive--;\r
5191 }\r
5192 \r
5193 /* Preprocess messages for buttons in main window */\r
5194 LRESULT CALLBACK\r
5195 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5196 {\r
5197   int id = GetWindowLong(hwnd, GWL_ID);\r
5198   int i, dir;\r
5199 \r
5200   for (i=0; i<N_BUTTONS; i++) {\r
5201     if (buttonDesc[i].id == id) break;\r
5202   }\r
5203   if (i == N_BUTTONS) return 0;\r
5204   switch (message) {\r
5205   case WM_KEYDOWN:\r
5206     switch (wParam) {\r
5207     case VK_LEFT:\r
5208     case VK_RIGHT:\r
5209       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5210       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5211       return TRUE;\r
5212     }\r
5213     break;\r
5214   case WM_CHAR:\r
5215     switch (wParam) {\r
5216     case '\r':\r
5217       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5218       return TRUE;\r
5219     case '\t':\r
5220       if (appData.icsActive) {\r
5221         if (GetKeyState(VK_SHIFT) < 0) {\r
5222           /* shifted */\r
5223           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5224           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5225           SetFocus(h);\r
5226         } else {\r
5227           /* unshifted */\r
5228           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5229           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5230           SetFocus(h);\r
5231         }\r
5232         return TRUE;\r
5233       }\r
5234       break;\r
5235     default:\r
5236       if (appData.icsActive) {\r
5237         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5238         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5239         SetFocus(h);\r
5240         SendMessage(h, WM_CHAR, wParam, lParam);\r
5241         return TRUE;\r
5242       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5243         PopUpMoveDialog((char)wParam);\r
5244       }\r
5245       break;\r
5246     }\r
5247     break;\r
5248   }\r
5249   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5250 }\r
5251 \r
5252 /* Process messages for Promotion dialog box */\r
5253 LRESULT CALLBACK\r
5254 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5255 {\r
5256   char promoChar;\r
5257 \r
5258   switch (message) {\r
5259   case WM_INITDIALOG: /* message: initialize dialog box */\r
5260     /* Center the dialog over the application window */\r
5261     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5262     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5263       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5264        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5265                SW_SHOW : SW_HIDE);\r
5266     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5267     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5268        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5269          PieceToChar(WhiteAngel) != '~') ||\r
5270         (PieceToChar(BlackAngel) >= 'A' &&\r
5271          PieceToChar(BlackAngel) != '~')   ) ?\r
5272                SW_SHOW : SW_HIDE);\r
5273     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5274        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5275          PieceToChar(WhiteMarshall) != '~') ||\r
5276         (PieceToChar(BlackMarshall) >= 'A' &&\r
5277          PieceToChar(BlackMarshall) != '~')   ) ?\r
5278                SW_SHOW : SW_HIDE);\r
5279     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5280     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5281        gameInfo.variant != VariantShogi ?\r
5282                SW_SHOW : SW_HIDE);\r
5283     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5284        gameInfo.variant != VariantShogi ?\r
5285                SW_SHOW : SW_HIDE);\r
5286     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5287        gameInfo.variant == VariantShogi ?\r
5288                SW_SHOW : SW_HIDE);\r
5289     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5290        gameInfo.variant == VariantShogi ?\r
5291                SW_SHOW : SW_HIDE);\r
5292     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5293        gameInfo.variant == VariantSuper ?\r
5294                SW_SHOW : SW_HIDE);\r
5295     return TRUE;\r
5296 \r
5297   case WM_COMMAND: /* message: received a command */\r
5298     switch (LOWORD(wParam)) {\r
5299     case IDCANCEL:\r
5300       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5301       ClearHighlights();\r
5302       DrawPosition(FALSE, NULL);\r
5303       return TRUE;\r
5304     case PB_King:\r
5305       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5306       break;\r
5307     case PB_Queen:\r
5308       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5309       break;\r
5310     case PB_Rook:\r
5311       promoChar = PieceToChar(BlackRook);\r
5312       break;\r
5313     case PB_Bishop:\r
5314       promoChar = PieceToChar(BlackBishop);\r
5315       break;\r
5316     case PB_Chancellor:\r
5317       promoChar = PieceToChar(BlackMarshall);\r
5318       break;\r
5319     case PB_Archbishop:\r
5320       promoChar = PieceToChar(BlackAngel);\r
5321       break;\r
5322     case PB_Knight:\r
5323       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5324       break;\r
5325     default:\r
5326       return FALSE;\r
5327     }\r
5328     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5329     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5330        only show the popup when we are already sure the move is valid or\r
5331        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5332        will figure out it is a promotion from the promoChar. */\r
5333     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5334     if (!appData.highlightLastMove) {\r
5335       ClearHighlights();\r
5336       DrawPosition(FALSE, NULL);\r
5337     }\r
5338     return TRUE;\r
5339   }\r
5340   return FALSE;\r
5341 }\r
5342 \r
5343 /* Pop up promotion dialog */\r
5344 VOID\r
5345 PromotionPopup(HWND hwnd)\r
5346 {\r
5347   FARPROC lpProc;\r
5348 \r
5349   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5350   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5351     hwnd, (DLGPROC)lpProc);\r
5352   FreeProcInstance(lpProc);\r
5353 }\r
5354 \r
5355 /* Toggle ShowThinking */\r
5356 VOID\r
5357 ToggleShowThinking()\r
5358 {\r
5359   appData.showThinking = !appData.showThinking;\r
5360   ShowThinkingEvent();\r
5361 }\r
5362 \r
5363 VOID\r
5364 LoadGameDialog(HWND hwnd, char* title)\r
5365 {\r
5366   UINT number = 0;\r
5367   FILE *f;\r
5368   char fileTitle[MSG_SIZ];\r
5369   f = OpenFileDialog(hwnd, "rb", "",\r
5370                      appData.oldSaveStyle ? "gam" : "pgn",\r
5371                      GAME_FILT,\r
5372                      title, &number, fileTitle, NULL);\r
5373   if (f != NULL) {\r
5374     cmailMsgLoaded = FALSE;\r
5375     if (number == 0) {\r
5376       int error = GameListBuild(f);\r
5377       if (error) {\r
5378         DisplayError("Cannot build game list", error);\r
5379       } else if (!ListEmpty(&gameList) &&\r
5380                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5381         GameListPopUp(f, fileTitle);\r
5382         return;\r
5383       }\r
5384       GameListDestroy();\r
5385       number = 1;\r
5386     }\r
5387     LoadGame(f, number, fileTitle, FALSE);\r
5388   }\r
5389 }\r
5390 \r
5391 VOID\r
5392 ChangedConsoleFont()\r
5393 {\r
5394   CHARFORMAT cfmt;\r
5395   CHARRANGE tmpsel, sel;\r
5396   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5397   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5398   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5399   PARAFORMAT paraf;\r
5400 \r
5401   cfmt.cbSize = sizeof(CHARFORMAT);\r
5402   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5403   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5404   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5405    * size.  This was undocumented in the version of MSVC++ that I had\r
5406    * when I wrote the code, but is apparently documented now.\r
5407    */\r
5408   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5409   cfmt.bCharSet = f->lf.lfCharSet;\r
5410   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5411   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5412   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5413   /* Why are the following seemingly needed too? */\r
5414   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5415   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5416   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5417   tmpsel.cpMin = 0;\r
5418   tmpsel.cpMax = -1; /*999999?*/\r
5419   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5420   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5421   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5422    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5423    */\r
5424   paraf.cbSize = sizeof(paraf);\r
5425   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5426   paraf.dxStartIndent = 0;\r
5427   paraf.dxOffset = WRAP_INDENT;\r
5428   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5429   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5430 }\r
5431 \r
5432 /*---------------------------------------------------------------------------*\\r
5433  *\r
5434  * Window Proc for main window\r
5435  *\r
5436 \*---------------------------------------------------------------------------*/\r
5437 \r
5438 /* Process messages for main window, etc. */\r
5439 LRESULT CALLBACK\r
5440 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5441 {\r
5442   FARPROC lpProc;\r
5443   int wmId, wmEvent;\r
5444   char *defName;\r
5445   FILE *f;\r
5446   UINT number;\r
5447   char fileTitle[MSG_SIZ];\r
5448   char buf[MSG_SIZ];\r
5449   static SnapData sd;\r
5450 \r
5451   switch (message) {\r
5452 \r
5453   case WM_PAINT: /* message: repaint portion of window */\r
5454     PaintProc(hwnd);\r
5455     break;\r
5456 \r
5457   case WM_ERASEBKGND:\r
5458     if (IsIconic(hwnd)) {\r
5459       /* Cheat; change the message */\r
5460       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5461     } else {\r
5462       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5463     }\r
5464     break;\r
5465 \r
5466   case WM_LBUTTONDOWN:\r
5467   case WM_MBUTTONDOWN:\r
5468   case WM_RBUTTONDOWN:\r
5469   case WM_LBUTTONUP:\r
5470   case WM_MBUTTONUP:\r
5471   case WM_RBUTTONUP:\r
5472   case WM_MOUSEMOVE:\r
5473   case WM_MOUSEWHEEL:\r
5474     MouseEvent(hwnd, message, wParam, lParam);\r
5475     break;\r
5476 \r
5477   case WM_CHAR:\r
5478     \r
5479     if (appData.icsActive) {\r
5480       if (wParam == '\t') {\r
5481         if (GetKeyState(VK_SHIFT) < 0) {\r
5482           /* shifted */\r
5483           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5484           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5485           SetFocus(h);\r
5486         } else {\r
5487           /* unshifted */\r
5488           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5489           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5490           SetFocus(h);\r
5491         }\r
5492       } else {\r
5493         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5494         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5495         SetFocus(h);\r
5496         SendMessage(h, message, wParam, lParam);\r
5497       }\r
5498     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5499       PopUpMoveDialog((char)wParam);\r
5500     }\r
5501     break;\r
5502 \r
5503   case WM_PALETTECHANGED:\r
5504     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5505       int nnew;\r
5506       HDC hdc = GetDC(hwndMain);\r
5507       SelectPalette(hdc, hPal, TRUE);\r
5508       nnew = RealizePalette(hdc);\r
5509       if (nnew > 0) {\r
5510         paletteChanged = TRUE;\r
5511 #if 0\r
5512         UpdateColors(hdc);\r
5513 #else\r
5514         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5515 #endif\r
5516       }\r
5517       ReleaseDC(hwnd, hdc);\r
5518     }\r
5519     break;\r
5520 \r
5521   case WM_QUERYNEWPALETTE:\r
5522     if (!appData.monoMode /*&& paletteChanged*/) {\r
5523       int nnew;\r
5524       HDC hdc = GetDC(hwndMain);\r
5525       paletteChanged = FALSE;\r
5526       SelectPalette(hdc, hPal, FALSE);\r
5527       nnew = RealizePalette(hdc);\r
5528       if (nnew > 0) {\r
5529         InvalidateRect(hwnd, &boardRect, FALSE);\r
5530       }\r
5531       ReleaseDC(hwnd, hdc);\r
5532       return TRUE;\r
5533     }\r
5534     return FALSE;\r
5535 \r
5536   case WM_COMMAND: /* message: command from application menu */\r
5537     wmId    = LOWORD(wParam);\r
5538     wmEvent = HIWORD(wParam);\r
5539 \r
5540     switch (wmId) {\r
5541     case IDM_NewGame:\r
5542       ResetGameEvent();\r
5543       AnalysisPopDown();\r
5544       break;\r
5545 \r
5546     case IDM_NewGameFRC:\r
5547       if( NewGameFRC() == 0 ) {\r
5548         ResetGameEvent();\r
5549         AnalysisPopDown();\r
5550       }\r
5551       break;\r
5552 \r
5553     case IDM_NewVariant:\r
5554       NewVariantPopup(hwnd);\r
5555       break;\r
5556 \r
5557     case IDM_LoadGame:\r
5558       LoadGameDialog(hwnd, "Load Game from File");\r
5559       break;\r
5560 \r
5561     case IDM_LoadNextGame:\r
5562       ReloadGame(1);\r
5563       break;\r
5564 \r
5565     case IDM_LoadPrevGame:\r
5566       ReloadGame(-1);\r
5567       break;\r
5568 \r
5569     case IDM_ReloadGame:\r
5570       ReloadGame(0);\r
5571       break;\r
5572 \r
5573     case IDM_LoadPosition:\r
5574       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5575         Reset(FALSE, TRUE);\r
5576       }\r
5577       number = 1;\r
5578       f = OpenFileDialog(hwnd, "rb", "",\r
5579                          appData.oldSaveStyle ? "pos" : "fen",\r
5580                          POSITION_FILT,\r
5581                          "Load Position from File", &number, fileTitle, NULL);\r
5582       if (f != NULL) {\r
5583         LoadPosition(f, number, fileTitle);\r
5584       }\r
5585       break;\r
5586 \r
5587     case IDM_LoadNextPosition:\r
5588       ReloadPosition(1);\r
5589       break;\r
5590 \r
5591     case IDM_LoadPrevPosition:\r
5592       ReloadPosition(-1);\r
5593       break;\r
5594 \r
5595     case IDM_ReloadPosition:\r
5596       ReloadPosition(0);\r
5597       break;\r
5598 \r
5599     case IDM_SaveGame:\r
5600       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5601       f = OpenFileDialog(hwnd, "a", defName,\r
5602                          appData.oldSaveStyle ? "gam" : "pgn",\r
5603                          GAME_FILT,\r
5604                          "Save Game to File", NULL, fileTitle, NULL);\r
5605       if (f != NULL) {\r
5606         SaveGame(f, 0, "");\r
5607       }\r
5608       break;\r
5609 \r
5610     case IDM_SavePosition:\r
5611       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5612       f = OpenFileDialog(hwnd, "a", defName,\r
5613                          appData.oldSaveStyle ? "pos" : "fen",\r
5614                          POSITION_FILT,\r
5615                          "Save Position to File", NULL, fileTitle, NULL);\r
5616       if (f != NULL) {\r
5617         SavePosition(f, 0, "");\r
5618       }\r
5619       break;\r
5620 \r
5621     case IDM_SaveDiagram:\r
5622       defName = "diagram";\r
5623       f = OpenFileDialog(hwnd, "wb", defName,\r
5624                          "bmp",\r
5625                          DIAGRAM_FILT,\r
5626                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5627       if (f != NULL) {\r
5628         SaveDiagram(f);\r
5629       }\r
5630       break;\r
5631 \r
5632     case IDM_CopyGame:\r
5633       CopyGameToClipboard();\r
5634       break;\r
5635 \r
5636     case IDM_PasteGame:\r
5637       PasteGameFromClipboard();\r
5638       break;\r
5639 \r
5640     case IDM_CopyGameListToClipboard:\r
5641       CopyGameListToClipboard();\r
5642       break;\r
5643 \r
5644     /* [AS] Autodetect FEN or PGN data */\r
5645     case IDM_PasteAny:\r
5646       PasteGameOrFENFromClipboard();\r
5647       break;\r
5648 \r
5649     /* [AS] Move history */\r
5650     case IDM_ShowMoveHistory:\r
5651         if( MoveHistoryIsUp() ) {\r
5652             MoveHistoryPopDown();\r
5653         }\r
5654         else {\r
5655             MoveHistoryPopUp();\r
5656         }\r
5657         break;\r
5658 \r
5659     /* [AS] Eval graph */\r
5660     case IDM_ShowEvalGraph:\r
5661         if( EvalGraphIsUp() ) {\r
5662             EvalGraphPopDown();\r
5663         }\r
5664         else {\r
5665             EvalGraphPopUp();\r
5666         }\r
5667         break;\r
5668 \r
5669     /* [AS] Engine output */\r
5670     case IDM_ShowEngineOutput:\r
5671         if( EngineOutputIsUp() ) {\r
5672             EngineOutputPopDown();\r
5673         }\r
5674         else {\r
5675             EngineOutputPopUp();\r
5676         }\r
5677         break;\r
5678 \r
5679     /* [AS] User adjudication */\r
5680     case IDM_UserAdjudication_White:\r
5681         UserAdjudicationEvent( +1 );\r
5682         break;\r
5683 \r
5684     case IDM_UserAdjudication_Black:\r
5685         UserAdjudicationEvent( -1 );\r
5686         break;\r
5687 \r
5688     case IDM_UserAdjudication_Draw:\r
5689         UserAdjudicationEvent( 0 );\r
5690         break;\r
5691 \r
5692     /* [AS] Game list options dialog */\r
5693     case IDM_GameListOptions:\r
5694       GameListOptions();\r
5695       break;\r
5696 \r
5697     case IDM_CopyPosition:\r
5698       CopyFENToClipboard();\r
5699       break;\r
5700 \r
5701     case IDM_PastePosition:\r
5702       PasteFENFromClipboard();\r
5703       break;\r
5704 \r
5705     case IDM_MailMove:\r
5706       MailMoveEvent();\r
5707       break;\r
5708 \r
5709     case IDM_ReloadCMailMsg:\r
5710       Reset(TRUE, TRUE);\r
5711       ReloadCmailMsgEvent(FALSE);\r
5712       break;\r
5713 \r
5714     case IDM_Minimize:\r
5715       ShowWindow(hwnd, SW_MINIMIZE);\r
5716       break;\r
5717 \r
5718     case IDM_Exit:\r
5719       ExitEvent(0);\r
5720       break;\r
5721 \r
5722     case IDM_MachineWhite:\r
5723       MachineWhiteEvent();\r
5724       /*\r
5725        * refresh the tags dialog only if it's visible\r
5726        */\r
5727       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5728           char *tags;\r
5729           tags = PGNTags(&gameInfo);\r
5730           TagsPopUp(tags, CmailMsg());\r
5731           free(tags);\r
5732       }\r
5733       break;\r
5734 \r
5735     case IDM_MachineBlack:\r
5736       MachineBlackEvent();\r
5737       /*\r
5738        * refresh the tags dialog only if it's visible\r
5739        */\r
5740       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5741           char *tags;\r
5742           tags = PGNTags(&gameInfo);\r
5743           TagsPopUp(tags, CmailMsg());\r
5744           free(tags);\r
5745       }\r
5746       break;\r
5747 \r
5748     case IDM_TwoMachines:\r
5749       TwoMachinesEvent();\r
5750       /*\r
5751        * refresh the tags dialog only if it's visible\r
5752        */\r
5753       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5754           char *tags;\r
5755           tags = PGNTags(&gameInfo);\r
5756           TagsPopUp(tags, CmailMsg());\r
5757           free(tags);\r
5758       }\r
5759       break;\r
5760 \r
5761     case IDM_AnalysisMode:\r
5762       if (!first.analysisSupport) {\r
5763         sprintf(buf, "%s does not support analysis", first.tidy);\r
5764         DisplayError(buf, 0);\r
5765       } else {\r
5766         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5767         if (appData.icsActive) {\r
5768                if (gameMode != IcsObserving) {\r
5769                        sprintf(buf, "You are not observing a game");\r
5770                        DisplayError(buf, 0);\r
5771                        /* secure check */\r
5772                        if (appData.icsEngineAnalyze) {\r
5773                                if (appData.debugMode) \r
5774                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5775                                ExitAnalyzeMode();\r
5776                                ModeHighlight();\r
5777                                break;\r
5778                        }\r
5779                        break;\r
5780                } else {\r
5781                        /* if enable, user want disable icsEngineAnalyze */\r
5782                        if (appData.icsEngineAnalyze) {\r
5783                                ExitAnalyzeMode();\r
5784                                ModeHighlight();\r
5785                                break;\r
5786                        }\r
5787                        appData.icsEngineAnalyze = TRUE;\r
5788                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5789                }\r
5790         } \r
5791         if (!appData.showThinking) ToggleShowThinking();\r
5792         AnalyzeModeEvent();\r
5793       }\r
5794       break;\r
5795 \r
5796     case IDM_AnalyzeFile:\r
5797       if (!first.analysisSupport) {\r
5798         char buf[MSG_SIZ];\r
5799         sprintf(buf, "%s does not support analysis", first.tidy);\r
5800         DisplayError(buf, 0);\r
5801       } else {\r
5802         if (!appData.showThinking) ToggleShowThinking();\r
5803         AnalyzeFileEvent();\r
5804         LoadGameDialog(hwnd, "Analyze Game from File");\r
5805         AnalysisPeriodicEvent(1);\r
5806       }\r
5807       break;\r
5808 \r
5809     case IDM_IcsClient:\r
5810       IcsClientEvent();\r
5811       break;\r
5812 \r
5813     case IDM_EditGame:\r
5814       EditGameEvent();\r
5815       break;\r
5816 \r
5817     case IDM_EditPosition:\r
5818       EditPositionEvent();\r
5819       break;\r
5820 \r
5821     case IDM_Training:\r
5822       TrainingEvent();\r
5823       break;\r
5824 \r
5825     case IDM_ShowGameList:\r
5826       ShowGameListProc();\r
5827       break;\r
5828 \r
5829     case IDM_EditTags:\r
5830       EditTagsProc();\r
5831       break;\r
5832 \r
5833     case IDM_EditComment:\r
5834       if (commentDialogUp && editComment) {\r
5835         CommentPopDown();\r
5836       } else {\r
5837         EditCommentEvent();\r
5838       }\r
5839       break;\r
5840 \r
5841     case IDM_Pause:\r
5842       PauseEvent();\r
5843       break;\r
5844 \r
5845     case IDM_Accept:\r
5846       AcceptEvent();\r
5847       break;\r
5848 \r
5849     case IDM_Decline:\r
5850       DeclineEvent();\r
5851       break;\r
5852 \r
5853     case IDM_Rematch:\r
5854       RematchEvent();\r
5855       break;\r
5856 \r
5857     case IDM_CallFlag:\r
5858       CallFlagEvent();\r
5859       break;\r
5860 \r
5861     case IDM_Draw:\r
5862       DrawEvent();\r
5863       break;\r
5864 \r
5865     case IDM_Adjourn:\r
5866       AdjournEvent();\r
5867       break;\r
5868 \r
5869     case IDM_Abort:\r
5870       AbortEvent();\r
5871       break;\r
5872 \r
5873     case IDM_Resign:\r
5874       ResignEvent();\r
5875       break;\r
5876 \r
5877     case IDM_StopObserving:\r
5878       StopObservingEvent();\r
5879       break;\r
5880 \r
5881     case IDM_StopExamining:\r
5882       StopExaminingEvent();\r
5883       break;\r
5884 \r
5885     case IDM_TypeInMove:\r
5886       PopUpMoveDialog('\000');\r
5887       break;\r
5888 \r
5889     case IDM_TypeInName:\r
5890       PopUpNameDialog('\000');\r
5891       break;\r
5892 \r
5893     case IDM_Backward:\r
5894       BackwardEvent();\r
5895       SetFocus(hwndMain);\r
5896       break;\r
5897 \r
5898     case IDM_Forward:\r
5899       ForwardEvent();\r
5900       SetFocus(hwndMain);\r
5901       break;\r
5902 \r
5903     case IDM_ToStart:\r
5904       ToStartEvent();\r
5905       SetFocus(hwndMain);\r
5906       break;\r
5907 \r
5908     case IDM_ToEnd:\r
5909       ToEndEvent();\r
5910       SetFocus(hwndMain);\r
5911       break;\r
5912 \r
5913     case IDM_Revert:\r
5914       RevertEvent();\r
5915       break;\r
5916 \r
5917     case IDM_TruncateGame:\r
5918       TruncateGameEvent();\r
5919       break;\r
5920 \r
5921     case IDM_MoveNow:\r
5922       MoveNowEvent();\r
5923       break;\r
5924 \r
5925     case IDM_RetractMove:\r
5926       RetractMoveEvent();\r
5927       break;\r
5928 \r
5929     case IDM_FlipView:\r
5930       flipView = !flipView;\r
5931       DrawPosition(FALSE, NULL);\r
5932       break;\r
5933 \r
5934     case IDM_FlipClock:\r
5935       flipClock = !flipClock;\r
5936       DisplayBothClocks();\r
5937       break;\r
5938 \r
5939     case IDM_GeneralOptions:\r
5940       GeneralOptionsPopup(hwnd);\r
5941       DrawPosition(TRUE, NULL);\r
5942       break;\r
5943 \r
5944     case IDM_BoardOptions:\r
5945       BoardOptionsPopup(hwnd);\r
5946       break;\r
5947 \r
5948     case IDM_EnginePlayOptions:\r
5949       EnginePlayOptionsPopup(hwnd);\r
5950       break;\r
5951 \r
5952     case IDM_OptionsUCI:\r
5953       UciOptionsPopup(hwnd);\r
5954       break;\r
5955 \r
5956     case IDM_IcsOptions:\r
5957       IcsOptionsPopup(hwnd);\r
5958       break;\r
5959 \r
5960     case IDM_Fonts:\r
5961       FontsOptionsPopup(hwnd);\r
5962       break;\r
5963 \r
5964     case IDM_Sounds:\r
5965       SoundOptionsPopup(hwnd);\r
5966       break;\r
5967 \r
5968     case IDM_CommPort:\r
5969       CommPortOptionsPopup(hwnd);\r
5970       break;\r
5971 \r
5972     case IDM_LoadOptions:\r
5973       LoadOptionsPopup(hwnd);\r
5974       break;\r
5975 \r
5976     case IDM_SaveOptions:\r
5977       SaveOptionsPopup(hwnd);\r
5978       break;\r
5979 \r
5980     case IDM_TimeControl:\r
5981       TimeControlOptionsPopup(hwnd);\r
5982       break;\r
5983 \r
5984     case IDM_SaveSettings:\r
5985       SaveSettings(settingsFileName);\r
5986       break;\r
5987 \r
5988     case IDM_SaveSettingsOnExit:\r
5989       saveSettingsOnExit = !saveSettingsOnExit;\r
5990       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5991                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5992                                          MF_CHECKED : MF_UNCHECKED));\r
5993       break;\r
5994 \r
5995     case IDM_Hint:\r
5996       HintEvent();\r
5997       break;\r
5998 \r
5999     case IDM_Book:\r
6000       BookEvent();\r
6001       break;\r
6002 \r
6003     case IDM_AboutGame:\r
6004       AboutGameEvent();\r
6005       break;\r
6006 \r
6007     case IDM_Debug:\r
6008       appData.debugMode = !appData.debugMode;\r
6009       if (appData.debugMode) {\r
6010         char dir[MSG_SIZ];\r
6011         GetCurrentDirectory(MSG_SIZ, dir);\r
6012         SetCurrentDirectory(installDir);\r
6013         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6014         SetCurrentDirectory(dir);\r
6015         setbuf(debugFP, NULL);\r
6016       } else {\r
6017         fclose(debugFP);\r
6018         debugFP = NULL;\r
6019       }\r
6020       break;\r
6021 \r
6022     case IDM_HELPCONTENTS:\r
6023       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6024         MessageBox (GetFocus(),\r
6025                     "Unable to activate help",\r
6026                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6027       }\r
6028       break;\r
6029 \r
6030     case IDM_HELPSEARCH:\r
6031       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6032         MessageBox (GetFocus(),\r
6033                     "Unable to activate help",\r
6034                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6035       }\r
6036       break;\r
6037 \r
6038     case IDM_HELPHELP:\r
6039       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6040         MessageBox (GetFocus(),\r
6041                     "Unable to activate help",\r
6042                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6043       }\r
6044       break;\r
6045 \r
6046     case IDM_ABOUT:\r
6047       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6048       DialogBox(hInst, \r
6049         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6050         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6051       FreeProcInstance(lpProc);\r
6052       break;\r
6053 \r
6054     case IDM_DirectCommand1:\r
6055       AskQuestionEvent("Direct Command",\r
6056                        "Send to chess program:", "", "1");\r
6057       break;\r
6058     case IDM_DirectCommand2:\r
6059       AskQuestionEvent("Direct Command",\r
6060                        "Send to second chess program:", "", "2");\r
6061       break;\r
6062 \r
6063     case EP_WhitePawn:\r
6064       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6065       fromX = fromY = -1;\r
6066       break;\r
6067 \r
6068     case EP_WhiteKnight:\r
6069       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6070       fromX = fromY = -1;\r
6071       break;\r
6072 \r
6073     case EP_WhiteBishop:\r
6074       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6075       fromX = fromY = -1;\r
6076       break;\r
6077 \r
6078     case EP_WhiteRook:\r
6079       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6080       fromX = fromY = -1;\r
6081       break;\r
6082 \r
6083     case EP_WhiteQueen:\r
6084       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6085       fromX = fromY = -1;\r
6086       break;\r
6087 \r
6088     case EP_WhiteFerz:\r
6089       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6090       fromX = fromY = -1;\r
6091       break;\r
6092 \r
6093     case EP_WhiteWazir:\r
6094       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6095       fromX = fromY = -1;\r
6096       break;\r
6097 \r
6098     case EP_WhiteAlfil:\r
6099       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6100       fromX = fromY = -1;\r
6101       break;\r
6102 \r
6103     case EP_WhiteCannon:\r
6104       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6105       fromX = fromY = -1;\r
6106       break;\r
6107 \r
6108     case EP_WhiteCardinal:\r
6109       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6110       fromX = fromY = -1;\r
6111       break;\r
6112 \r
6113     case EP_WhiteMarshall:\r
6114       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6115       fromX = fromY = -1;\r
6116       break;\r
6117 \r
6118     case EP_WhiteKing:\r
6119       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6120       fromX = fromY = -1;\r
6121       break;\r
6122 \r
6123     case EP_BlackPawn:\r
6124       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6125       fromX = fromY = -1;\r
6126       break;\r
6127 \r
6128     case EP_BlackKnight:\r
6129       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6130       fromX = fromY = -1;\r
6131       break;\r
6132 \r
6133     case EP_BlackBishop:\r
6134       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6135       fromX = fromY = -1;\r
6136       break;\r
6137 \r
6138     case EP_BlackRook:\r
6139       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6140       fromX = fromY = -1;\r
6141       break;\r
6142 \r
6143     case EP_BlackQueen:\r
6144       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6145       fromX = fromY = -1;\r
6146       break;\r
6147 \r
6148     case EP_BlackFerz:\r
6149       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6150       fromX = fromY = -1;\r
6151       break;\r
6152 \r
6153     case EP_BlackWazir:\r
6154       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6155       fromX = fromY = -1;\r
6156       break;\r
6157 \r
6158     case EP_BlackAlfil:\r
6159       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6160       fromX = fromY = -1;\r
6161       break;\r
6162 \r
6163     case EP_BlackCannon:\r
6164       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6165       fromX = fromY = -1;\r
6166       break;\r
6167 \r
6168     case EP_BlackCardinal:\r
6169       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6170       fromX = fromY = -1;\r
6171       break;\r
6172 \r
6173     case EP_BlackMarshall:\r
6174       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6175       fromX = fromY = -1;\r
6176       break;\r
6177 \r
6178     case EP_BlackKing:\r
6179       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6180       fromX = fromY = -1;\r
6181       break;\r
6182 \r
6183     case EP_EmptySquare:\r
6184       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6185       fromX = fromY = -1;\r
6186       break;\r
6187 \r
6188     case EP_ClearBoard:\r
6189       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6190       fromX = fromY = -1;\r
6191       break;\r
6192 \r
6193     case EP_White:\r
6194       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6195       fromX = fromY = -1;\r
6196       break;\r
6197 \r
6198     case EP_Black:\r
6199       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6200       fromX = fromY = -1;\r
6201       break;\r
6202 \r
6203     case EP_Promote:\r
6204       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6205       fromX = fromY = -1;\r
6206       break;\r
6207 \r
6208     case EP_Demote:\r
6209       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6210       fromX = fromY = -1;\r
6211       break;\r
6212 \r
6213     case DP_Pawn:\r
6214       DropMenuEvent(WhitePawn, fromX, fromY);\r
6215       fromX = fromY = -1;\r
6216       break;\r
6217 \r
6218     case DP_Knight:\r
6219       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6220       fromX = fromY = -1;\r
6221       break;\r
6222 \r
6223     case DP_Bishop:\r
6224       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6225       fromX = fromY = -1;\r
6226       break;\r
6227 \r
6228     case DP_Rook:\r
6229       DropMenuEvent(WhiteRook, fromX, fromY);\r
6230       fromX = fromY = -1;\r
6231       break;\r
6232 \r
6233     case DP_Queen:\r
6234       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6235       fromX = fromY = -1;\r
6236       break;\r
6237 \r
6238     default:\r
6239       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6240     }\r
6241     break;\r
6242 \r
6243   case WM_TIMER:\r
6244     switch (wParam) {\r
6245     case CLOCK_TIMER_ID:\r
6246       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6247       clockTimerEvent = 0;\r
6248       DecrementClocks(); /* call into back end */\r
6249       break;\r
6250     case LOAD_GAME_TIMER_ID:\r
6251       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6252       loadGameTimerEvent = 0;\r
6253       AutoPlayGameLoop(); /* call into back end */\r
6254       break;\r
6255     case ANALYSIS_TIMER_ID:\r
6256       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6257                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6258         AnalysisPeriodicEvent(0);\r
6259       } else {\r
6260         KillTimer(hwnd, analysisTimerEvent);\r
6261         analysisTimerEvent = 0;\r
6262       }\r
6263       break;\r
6264     case DELAYED_TIMER_ID:\r
6265       KillTimer(hwnd, delayedTimerEvent);\r
6266       delayedTimerEvent = 0;\r
6267       delayedTimerCallback();\r
6268       break;\r
6269     }\r
6270     break;\r
6271 \r
6272   case WM_USER_Input:\r
6273     InputEvent(hwnd, message, wParam, lParam);\r
6274     break;\r
6275 \r
6276   /* [AS] Also move "attached" child windows */\r
6277   case WM_WINDOWPOSCHANGING:\r
6278 \r
6279     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6280         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6281 \r
6282         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6283             /* Window is moving */\r
6284             RECT rcMain;\r
6285 \r
6286 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6287             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6288             rcMain.right  = boardX + winWidth;\r
6289             rcMain.top    = boardY;\r
6290             rcMain.bottom = boardY + winHeight;\r
6291             \r
6292             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6293             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6294             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6295             boardX = lpwp->x;\r
6296             boardY = lpwp->y;\r
6297         }\r
6298     }\r
6299     break;\r
6300 \r
6301   /* [AS] Snapping */\r
6302   case WM_ENTERSIZEMOVE:\r
6303     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6304     if (hwnd == hwndMain) {\r
6305       doingSizing = TRUE;\r
6306       lastSizing = 0;\r
6307     }\r
6308     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6309     break;\r
6310 \r
6311   case WM_SIZING:\r
6312     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6313     if (hwnd == hwndMain) {\r
6314       lastSizing = wParam;\r
6315     }\r
6316     break;\r
6317 \r
6318   case WM_MOVING:\r
6319     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6320       return OnMoving( &sd, hwnd, wParam, lParam );\r
6321 \r
6322   case WM_EXITSIZEMOVE:\r
6323     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6324     if (hwnd == hwndMain) {\r
6325       RECT client;\r
6326       doingSizing = FALSE;\r
6327       InvalidateRect(hwnd, &boardRect, FALSE);\r
6328       GetClientRect(hwnd, &client);\r
6329       ResizeBoard(client.right, client.bottom, lastSizing);\r
6330       lastSizing = 0;\r
6331       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6332     }\r
6333     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6334     break;\r
6335 \r
6336   case WM_DESTROY: /* message: window being destroyed */\r
6337     PostQuitMessage(0);\r
6338     break;\r
6339 \r
6340   case WM_CLOSE:\r
6341     if (hwnd == hwndMain) {\r
6342       ExitEvent(0);\r
6343     }\r
6344     break;\r
6345 \r
6346   default:      /* Passes it on if unprocessed */\r
6347     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6348   }\r
6349   return 0;\r
6350 }\r
6351 \r
6352 /*---------------------------------------------------------------------------*\\r
6353  *\r
6354  * Misc utility routines\r
6355  *\r
6356 \*---------------------------------------------------------------------------*/\r
6357 \r
6358 /*\r
6359  * Decent random number generator, at least not as bad as Windows\r
6360  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6361  */\r
6362 unsigned int randstate;\r
6363 \r
6364 int\r
6365 myrandom(void)\r
6366 {\r
6367   randstate = randstate * 1664525 + 1013904223;\r
6368   return (int) randstate & 0x7fffffff;\r
6369 }\r
6370 \r
6371 void\r
6372 mysrandom(unsigned int seed)\r
6373 {\r
6374   randstate = seed;\r
6375 }\r
6376 \r
6377 \r
6378 /* \r
6379  * returns TRUE if user selects a different color, FALSE otherwise \r
6380  */\r
6381 \r
6382 BOOL\r
6383 ChangeColor(HWND hwnd, COLORREF *which)\r
6384 {\r
6385   static BOOL firstTime = TRUE;\r
6386   static DWORD customColors[16];\r
6387   CHOOSECOLOR cc;\r
6388   COLORREF newcolor;\r
6389   int i;\r
6390   ColorClass ccl;\r
6391 \r
6392   if (firstTime) {\r
6393     /* Make initial colors in use available as custom colors */\r
6394     /* Should we put the compiled-in defaults here instead? */\r
6395     i = 0;\r
6396     customColors[i++] = lightSquareColor & 0xffffff;\r
6397     customColors[i++] = darkSquareColor & 0xffffff;\r
6398     customColors[i++] = whitePieceColor & 0xffffff;\r
6399     customColors[i++] = blackPieceColor & 0xffffff;\r
6400     customColors[i++] = highlightSquareColor & 0xffffff;\r
6401     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6402 \r
6403     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6404       customColors[i++] = textAttribs[ccl].color;\r
6405     }\r
6406     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6407     firstTime = FALSE;\r
6408   }\r
6409 \r
6410   cc.lStructSize = sizeof(cc);\r
6411   cc.hwndOwner = hwnd;\r
6412   cc.hInstance = NULL;\r
6413   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6414   cc.lpCustColors = (LPDWORD) customColors;\r
6415   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6416 \r
6417   if (!ChooseColor(&cc)) return FALSE;\r
6418 \r
6419   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6420   if (newcolor == *which) return FALSE;\r
6421   *which = newcolor;\r
6422   return TRUE;\r
6423 \r
6424   /*\r
6425   InitDrawingColors();\r
6426   InvalidateRect(hwnd, &boardRect, FALSE);\r
6427   */\r
6428 }\r
6429 \r
6430 BOOLEAN\r
6431 MyLoadSound(MySound *ms)\r
6432 {\r
6433   BOOL ok = FALSE;\r
6434   struct stat st;\r
6435   FILE *f;\r
6436 \r
6437   if (ms->data) free(ms->data);\r
6438   ms->data = NULL;\r
6439 \r
6440   switch (ms->name[0]) {\r
6441   case NULLCHAR:\r
6442     /* Silence */\r
6443     ok = TRUE;\r
6444     break;\r
6445   case '$':\r
6446     /* System sound from Control Panel.  Don't preload here. */\r
6447     ok = TRUE;\r
6448     break;\r
6449   case '!':\r
6450     if (ms->name[1] == NULLCHAR) {\r
6451       /* "!" alone = silence */\r
6452       ok = TRUE;\r
6453     } else {\r
6454       /* Builtin wave resource.  Error if not found. */\r
6455       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6456       if (h == NULL) break;\r
6457       ms->data = (void *)LoadResource(hInst, h);\r
6458       if (h == NULL) break;\r
6459       ok = TRUE;\r
6460     }\r
6461     break;\r
6462   default:\r
6463     /* .wav file.  Error if not found. */\r
6464     f = fopen(ms->name, "rb");\r
6465     if (f == NULL) break;\r
6466     if (fstat(fileno(f), &st) < 0) break;\r
6467     ms->data = malloc(st.st_size);\r
6468     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6469     fclose(f);\r
6470     ok = TRUE;\r
6471     break;\r
6472   }\r
6473   if (!ok) {\r
6474     char buf[MSG_SIZ];\r
6475     sprintf(buf, "Error loading sound %s", ms->name);\r
6476     DisplayError(buf, GetLastError());\r
6477   }\r
6478   return ok;\r
6479 }\r
6480 \r
6481 BOOLEAN\r
6482 MyPlaySound(MySound *ms)\r
6483 {\r
6484   BOOLEAN ok = FALSE;\r
6485   switch (ms->name[0]) {\r
6486   case NULLCHAR:\r
6487     /* Silence */\r
6488     ok = TRUE;\r
6489     break;\r
6490   case '$':\r
6491     /* System sound from Control Panel (deprecated feature).\r
6492        "$" alone or an unset sound name gets default beep (still in use). */\r
6493     if (ms->name[1]) {\r
6494       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6495     }\r
6496     if (!ok) ok = MessageBeep(MB_OK);\r
6497     break; \r
6498   case '!':\r
6499     /* Builtin wave resource, or "!" alone for silence */\r
6500     if (ms->name[1]) {\r
6501       if (ms->data == NULL) return FALSE;\r
6502       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6503     } else {\r
6504       ok = TRUE;\r
6505     }\r
6506     break;\r
6507   default:\r
6508     /* .wav file.  Error if not found. */\r
6509     if (ms->data == NULL) return FALSE;\r
6510     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6511     break;\r
6512   }\r
6513   /* Don't print an error: this can happen innocently if the sound driver\r
6514      is busy; for instance, if another instance of WinBoard is playing\r
6515      a sound at about the same time. */\r
6516 #if 0\r
6517   if (!ok) {\r
6518     char buf[MSG_SIZ];\r
6519     sprintf(buf, "Error playing sound %s", ms->name);\r
6520     DisplayError(buf, GetLastError());\r
6521   }\r
6522 #endif\r
6523   return ok;\r
6524 }\r
6525 \r
6526 \r
6527 LRESULT CALLBACK\r
6528 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6529 {\r
6530   BOOL ok;\r
6531   OPENFILENAME *ofn;\r
6532   static UINT *number; /* gross that this is static */\r
6533 \r
6534   switch (message) {\r
6535   case WM_INITDIALOG: /* message: initialize dialog box */\r
6536     /* Center the dialog over the application window */\r
6537     ofn = (OPENFILENAME *) lParam;\r
6538     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6539       number = (UINT *) ofn->lCustData;\r
6540       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6541     } else {\r
6542       number = NULL;\r
6543     }\r
6544     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6545     return FALSE;  /* Allow for further processing */\r
6546 \r
6547   case WM_COMMAND:\r
6548     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6549       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6550     }\r
6551     return FALSE;  /* Allow for further processing */\r
6552   }\r
6553   return FALSE;\r
6554 }\r
6555 \r
6556 UINT APIENTRY\r
6557 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6558 {\r
6559   static UINT *number;\r
6560   OPENFILENAME *ofname;\r
6561   OFNOTIFY *ofnot;\r
6562   switch (uiMsg) {\r
6563   case WM_INITDIALOG:\r
6564     ofname = (OPENFILENAME *)lParam;\r
6565     number = (UINT *)(ofname->lCustData);\r
6566     break;\r
6567   case WM_NOTIFY:\r
6568     ofnot = (OFNOTIFY *)lParam;\r
6569     if (ofnot->hdr.code == CDN_FILEOK) {\r
6570       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6571     }\r
6572     break;\r
6573   }\r
6574   return 0;\r
6575 }\r
6576 \r
6577 \r
6578 FILE *\r
6579 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6580                char *nameFilt, char *dlgTitle, UINT *number,\r
6581                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6582 {\r
6583   OPENFILENAME openFileName;\r
6584   char buf1[MSG_SIZ];\r
6585   FILE *f;\r
6586 \r
6587   if (fileName == NULL) fileName = buf1;\r
6588   if (defName == NULL) {\r
6589     strcpy(fileName, "*.");\r
6590     strcat(fileName, defExt);\r
6591   } else {\r
6592     strcpy(fileName, defName);\r
6593   }\r
6594   if (fileTitle) strcpy(fileTitle, "");\r
6595   if (number) *number = 0;\r
6596 \r
6597   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6598   openFileName.hwndOwner         = hwnd;\r
6599   openFileName.hInstance         = (HANDLE) hInst;\r
6600   openFileName.lpstrFilter       = nameFilt;\r
6601   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6602   openFileName.nMaxCustFilter    = 0L;\r
6603   openFileName.nFilterIndex      = 1L;\r
6604   openFileName.lpstrFile         = fileName;\r
6605   openFileName.nMaxFile          = MSG_SIZ;\r
6606   openFileName.lpstrFileTitle    = fileTitle;\r
6607   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6608   openFileName.lpstrInitialDir   = NULL;\r
6609   openFileName.lpstrTitle        = dlgTitle;\r
6610   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6611     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6612     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6613     | (oldDialog ? 0 : OFN_EXPLORER);\r
6614   openFileName.nFileOffset       = 0;\r
6615   openFileName.nFileExtension    = 0;\r
6616   openFileName.lpstrDefExt       = defExt;\r
6617   openFileName.lCustData         = (LONG) number;\r
6618   openFileName.lpfnHook          = oldDialog ?\r
6619     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6620   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6621 \r
6622   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6623                         GetOpenFileName(&openFileName)) {\r
6624     /* open the file */\r
6625     f = fopen(openFileName.lpstrFile, write);\r
6626     if (f == NULL) {\r
6627       MessageBox(hwnd, "File open failed", NULL,\r
6628                  MB_OK|MB_ICONEXCLAMATION);\r
6629       return NULL;\r
6630     }\r
6631   } else {\r
6632     int err = CommDlgExtendedError();\r
6633     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6634     return FALSE;\r
6635   }\r
6636   return f;\r
6637 }\r
6638 \r
6639 \r
6640 \r
6641 VOID APIENTRY\r
6642 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6643 {\r
6644   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6645 \r
6646   /*\r
6647    * Get the first pop-up menu in the menu template. This is the\r
6648    * menu that TrackPopupMenu displays.\r
6649    */\r
6650   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6651 \r
6652   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6653 \r
6654   /*\r
6655    * TrackPopup uses screen coordinates, so convert the\r
6656    * coordinates of the mouse click to screen coordinates.\r
6657    */\r
6658   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6659 \r
6660   /* Draw and track the floating pop-up menu. */\r
6661   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6662                  pt.x, pt.y, 0, hwnd, NULL);\r
6663 \r
6664   /* Destroy the menu.*/\r
6665   DestroyMenu(hmenu);\r
6666 }\r
6667    \r
6668 typedef struct {\r
6669   HWND hDlg, hText;\r
6670   int sizeX, sizeY, newSizeX, newSizeY;\r
6671   HDWP hdwp;\r
6672 } ResizeEditPlusButtonsClosure;\r
6673 \r
6674 BOOL CALLBACK\r
6675 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6676 {\r
6677   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6678   RECT rect;\r
6679   POINT pt;\r
6680 \r
6681   if (hChild == cl->hText) return TRUE;\r
6682   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6683   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6684   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6685   ScreenToClient(cl->hDlg, &pt);\r
6686   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6687     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6688   return TRUE;\r
6689 }\r
6690 \r
6691 /* Resize a dialog that has a (rich) edit field filling most of\r
6692    the top, with a row of buttons below */\r
6693 VOID\r
6694 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6695 {\r
6696   RECT rectText;\r
6697   int newTextHeight, newTextWidth;\r
6698   ResizeEditPlusButtonsClosure cl;\r
6699   \r
6700   /*if (IsIconic(hDlg)) return;*/\r
6701   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6702   \r
6703   cl.hdwp = BeginDeferWindowPos(8);\r
6704 \r
6705   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6706   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6707   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6708   if (newTextHeight < 0) {\r
6709     newSizeY += -newTextHeight;\r
6710     newTextHeight = 0;\r
6711   }\r
6712   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6713     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6714 \r
6715   cl.hDlg = hDlg;\r
6716   cl.hText = hText;\r
6717   cl.sizeX = sizeX;\r
6718   cl.sizeY = sizeY;\r
6719   cl.newSizeX = newSizeX;\r
6720   cl.newSizeY = newSizeY;\r
6721   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6722 \r
6723   EndDeferWindowPos(cl.hdwp);\r
6724 }\r
6725 \r
6726 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6727 {\r
6728     RECT    rChild, rParent;\r
6729     int     wChild, hChild, wParent, hParent;\r
6730     int     wScreen, hScreen, xNew, yNew;\r
6731     HDC     hdc;\r
6732 \r
6733     /* Get the Height and Width of the child window */\r
6734     GetWindowRect (hwndChild, &rChild);\r
6735     wChild = rChild.right - rChild.left;\r
6736     hChild = rChild.bottom - rChild.top;\r
6737 \r
6738     /* Get the Height and Width of the parent window */\r
6739     GetWindowRect (hwndParent, &rParent);\r
6740     wParent = rParent.right - rParent.left;\r
6741     hParent = rParent.bottom - rParent.top;\r
6742 \r
6743     /* Get the display limits */\r
6744     hdc = GetDC (hwndChild);\r
6745     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6746     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6747     ReleaseDC(hwndChild, hdc);\r
6748 \r
6749     /* Calculate new X position, then adjust for screen */\r
6750     xNew = rParent.left + ((wParent - wChild) /2);\r
6751     if (xNew < 0) {\r
6752         xNew = 0;\r
6753     } else if ((xNew+wChild) > wScreen) {\r
6754         xNew = wScreen - wChild;\r
6755     }\r
6756 \r
6757     /* Calculate new Y position, then adjust for screen */\r
6758     if( mode == 0 ) {\r
6759         yNew = rParent.top  + ((hParent - hChild) /2);\r
6760     }\r
6761     else {\r
6762         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6763     }\r
6764 \r
6765     if (yNew < 0) {\r
6766         yNew = 0;\r
6767     } else if ((yNew+hChild) > hScreen) {\r
6768         yNew = hScreen - hChild;\r
6769     }\r
6770 \r
6771     /* Set it, and return */\r
6772     return SetWindowPos (hwndChild, NULL,\r
6773                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6774 }\r
6775 \r
6776 /* Center one window over another */\r
6777 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6778 {\r
6779     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6780 }\r
6781 \r
6782 /*---------------------------------------------------------------------------*\\r
6783  *\r
6784  * Startup Dialog functions\r
6785  *\r
6786 \*---------------------------------------------------------------------------*/\r
6787 void\r
6788 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6789 {\r
6790   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6791 \r
6792   while (*cd != NULL) {\r
6793     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6794     cd++;\r
6795   }\r
6796 }\r
6797 \r
6798 void\r
6799 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6800 {\r
6801   char buf1[ARG_MAX];\r
6802   int len;\r
6803 \r
6804   if (str[0] == '@') {\r
6805     FILE* f = fopen(str + 1, "r");\r
6806     if (f == NULL) {\r
6807       DisplayFatalError(str + 1, errno, 2);\r
6808       return;\r
6809     }\r
6810     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6811     fclose(f);\r
6812     buf1[len] = NULLCHAR;\r
6813     str = buf1;\r
6814   }\r
6815 \r
6816   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6817 \r
6818   for (;;) {\r
6819     char buf[MSG_SIZ];\r
6820     char *end = strchr(str, '\n');\r
6821     if (end == NULL) return;\r
6822     memcpy(buf, str, end - str);\r
6823     buf[end - str] = NULLCHAR;\r
6824     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6825     str = end + 1;\r
6826   }\r
6827 }\r
6828 \r
6829 void\r
6830 SetStartupDialogEnables(HWND hDlg)\r
6831 {\r
6832   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6833     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6834     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6835   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6836     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6837   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6838     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6839   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6840     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6841   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6842     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6843     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6844     IsDlgButtonChecked(hDlg, OPT_View));\r
6845 }\r
6846 \r
6847 char *\r
6848 QuoteForFilename(char *filename)\r
6849 {\r
6850   int dquote, space;\r
6851   dquote = strchr(filename, '"') != NULL;\r
6852   space = strchr(filename, ' ') != NULL;\r
6853   if (dquote || space) {\r
6854     if (dquote) {\r
6855       return "'";\r
6856     } else {\r
6857       return "\"";\r
6858     }\r
6859   } else {\r
6860     return "";\r
6861   }\r
6862 }\r
6863 \r
6864 VOID\r
6865 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6866 {\r
6867   char buf[MSG_SIZ];\r
6868   char *q;\r
6869 \r
6870   InitComboStringsFromOption(hwndCombo, nthnames);\r
6871   q = QuoteForFilename(nthcp);\r
6872   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6873   if (*nthdir != NULLCHAR) {\r
6874     q = QuoteForFilename(nthdir);\r
6875     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6876   }\r
6877   if (*nthcp == NULLCHAR) {\r
6878     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6879   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6880     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6881     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6882   }\r
6883 }\r
6884 \r
6885 LRESULT CALLBACK\r
6886 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6887 {\r
6888   char buf[MSG_SIZ];\r
6889   HANDLE hwndCombo;\r
6890   char *p;\r
6891 \r
6892   switch (message) {\r
6893   case WM_INITDIALOG:\r
6894     /* Center the dialog */\r
6895     CenterWindow (hDlg, GetDesktopWindow());\r
6896     /* Initialize the dialog items */\r
6897     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6898                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6899                   firstChessProgramNames);\r
6900     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6901                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6902                   secondChessProgramNames);\r
6903     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6904     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6905     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6906     if (*appData.icsHelper != NULLCHAR) {\r
6907       char *q = QuoteForFilename(appData.icsHelper);\r
6908       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6909     }\r
6910     if (*appData.icsHost == NULLCHAR) {\r
6911       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6912       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6913     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6914       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6915       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6916     }\r
6917 \r
6918     if (appData.icsActive) {\r
6919       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6920     }\r
6921     else if (appData.noChessProgram) {\r
6922       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6923     }\r
6924     else {\r
6925       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6926     }\r
6927 \r
6928     SetStartupDialogEnables(hDlg);\r
6929     return TRUE;\r
6930 \r
6931   case WM_COMMAND:\r
6932     switch (LOWORD(wParam)) {\r
6933     case IDOK:\r
6934       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6935         strcpy(buf, "/fcp=");\r
6936         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6937         p = buf;\r
6938         ParseArgs(StringGet, &p);\r
6939         strcpy(buf, "/scp=");\r
6940         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6941         p = buf;\r
6942         ParseArgs(StringGet, &p);\r
6943         appData.noChessProgram = FALSE;\r
6944         appData.icsActive = FALSE;\r
6945       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6946         strcpy(buf, "/ics /icshost=");\r
6947         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6948         p = buf;\r
6949         ParseArgs(StringGet, &p);\r
6950         if (appData.zippyPlay) {\r
6951           strcpy(buf, "/fcp=");\r
6952           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6953           p = buf;\r
6954           ParseArgs(StringGet, &p);\r
6955         }\r
6956       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6957         appData.noChessProgram = TRUE;\r
6958         appData.icsActive = FALSE;\r
6959       } else {\r
6960         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6961                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6962         return TRUE;\r
6963       }\r
6964       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6965         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6966         p = buf;\r
6967         ParseArgs(StringGet, &p);\r
6968       }\r
6969       EndDialog(hDlg, TRUE);\r
6970       return TRUE;\r
6971 \r
6972     case IDCANCEL:\r
6973       ExitEvent(0);\r
6974       return TRUE;\r
6975 \r
6976     case IDM_HELPCONTENTS:\r
6977       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6978         MessageBox (GetFocus(),\r
6979                     "Unable to activate help",\r
6980                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6981       }\r
6982       break;\r
6983 \r
6984     default:\r
6985       SetStartupDialogEnables(hDlg);\r
6986       break;\r
6987     }\r
6988     break;\r
6989   }\r
6990   return FALSE;\r
6991 }\r
6992 \r
6993 /*---------------------------------------------------------------------------*\\r
6994  *\r
6995  * About box dialog functions\r
6996  *\r
6997 \*---------------------------------------------------------------------------*/\r
6998 \r
6999 /* Process messages for "About" dialog box */\r
7000 LRESULT CALLBACK\r
7001 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7002 {\r
7003   switch (message) {\r
7004   case WM_INITDIALOG: /* message: initialize dialog box */\r
7005     /* Center the dialog over the application window */\r
7006     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7007     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7008     return (TRUE);\r
7009 \r
7010   case WM_COMMAND: /* message: received a command */\r
7011     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7012         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7013       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7014       return (TRUE);\r
7015     }\r
7016     break;\r
7017   }\r
7018   return (FALSE);\r
7019 }\r
7020 \r
7021 /*---------------------------------------------------------------------------*\\r
7022  *\r
7023  * Comment Dialog functions\r
7024  *\r
7025 \*---------------------------------------------------------------------------*/\r
7026 \r
7027 LRESULT CALLBACK\r
7028 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7029 {\r
7030   static HANDLE hwndText = NULL;\r
7031   int len, newSizeX, newSizeY, flags;\r
7032   static int sizeX, sizeY;\r
7033   char *str;\r
7034   RECT rect;\r
7035   MINMAXINFO *mmi;\r
7036 \r
7037   switch (message) {\r
7038   case WM_INITDIALOG: /* message: initialize dialog box */\r
7039     /* Initialize the dialog items */\r
7040     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7041     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7042     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7043     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7044     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7045     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7046     SetWindowText(hDlg, commentTitle);\r
7047     if (editComment) {\r
7048       SetFocus(hwndText);\r
7049     } else {\r
7050       SetFocus(GetDlgItem(hDlg, IDOK));\r
7051     }\r
7052     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7053                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7054                 MAKELPARAM(FALSE, 0));\r
7055     /* Size and position the dialog */\r
7056     if (!commentDialog) {\r
7057       commentDialog = hDlg;\r
7058       flags = SWP_NOZORDER;\r
7059       GetClientRect(hDlg, &rect);\r
7060       sizeX = rect.right;\r
7061       sizeY = rect.bottom;\r
7062       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7063           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7064         WINDOWPLACEMENT wp;\r
7065         EnsureOnScreen(&commentX, &commentY);\r
7066         wp.length = sizeof(WINDOWPLACEMENT);\r
7067         wp.flags = 0;\r
7068         wp.showCmd = SW_SHOW;\r
7069         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7070         wp.rcNormalPosition.left = commentX;\r
7071         wp.rcNormalPosition.right = commentX + commentW;\r
7072         wp.rcNormalPosition.top = commentY;\r
7073         wp.rcNormalPosition.bottom = commentY + commentH;\r
7074         SetWindowPlacement(hDlg, &wp);\r
7075 \r
7076         GetClientRect(hDlg, &rect);\r
7077         newSizeX = rect.right;\r
7078         newSizeY = rect.bottom;\r
7079         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7080                               newSizeX, newSizeY);\r
7081         sizeX = newSizeX;\r
7082         sizeY = newSizeY;\r
7083       }\r
7084     }\r
7085     return FALSE;\r
7086 \r
7087   case WM_COMMAND: /* message: received a command */\r
7088     switch (LOWORD(wParam)) {\r
7089     case IDOK:\r
7090       if (editComment) {\r
7091         char *p, *q;\r
7092         /* Read changed options from the dialog box */\r
7093         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7094         len = GetWindowTextLength(hwndText);\r
7095         str = (char *) malloc(len + 1);\r
7096         GetWindowText(hwndText, str, len + 1);\r
7097         p = q = str;\r
7098         while (*q) {\r
7099           if (*q == '\r')\r
7100             q++;\r
7101           else\r
7102             *p++ = *q++;\r
7103         }\r
7104         *p = NULLCHAR;\r
7105         ReplaceComment(commentIndex, str);\r
7106         free(str);\r
7107       }\r
7108       CommentPopDown();\r
7109       return TRUE;\r
7110 \r
7111     case IDCANCEL:\r
7112     case OPT_CancelComment:\r
7113       CommentPopDown();\r
7114       return TRUE;\r
7115 \r
7116     case OPT_ClearComment:\r
7117       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7118       break;\r
7119 \r
7120     case OPT_EditComment:\r
7121       EditCommentEvent();\r
7122       return TRUE;\r
7123 \r
7124     default:\r
7125       break;\r
7126     }\r
7127     break;\r
7128 \r
7129   case WM_SIZE:\r
7130     newSizeX = LOWORD(lParam);\r
7131     newSizeY = HIWORD(lParam);\r
7132     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7133     sizeX = newSizeX;\r
7134     sizeY = newSizeY;\r
7135     break;\r
7136 \r
7137   case WM_GETMINMAXINFO:\r
7138     /* Prevent resizing window too small */\r
7139     mmi = (MINMAXINFO *) lParam;\r
7140     mmi->ptMinTrackSize.x = 100;\r
7141     mmi->ptMinTrackSize.y = 100;\r
7142     break;\r
7143   }\r
7144   return FALSE;\r
7145 }\r
7146 \r
7147 VOID\r
7148 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7149 {\r
7150   FARPROC lpProc;\r
7151   char *p, *q;\r
7152 \r
7153   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7154 \r
7155   if (str == NULL) str = "";\r
7156   p = (char *) malloc(2 * strlen(str) + 2);\r
7157   q = p;\r
7158   while (*str) {\r
7159     if (*str == '\n') *q++ = '\r';\r
7160     *q++ = *str++;\r
7161   }\r
7162   *q = NULLCHAR;\r
7163   if (commentText != NULL) free(commentText);\r
7164 \r
7165   commentIndex = index;\r
7166   commentTitle = title;\r
7167   commentText = p;\r
7168   editComment = edit;\r
7169 \r
7170   if (commentDialog) {\r
7171     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7172     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7173   } else {\r
7174     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7175     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7176                  hwndMain, (DLGPROC)lpProc);\r
7177     FreeProcInstance(lpProc);\r
7178   }\r
7179   commentDialogUp = TRUE;\r
7180 }\r
7181 \r
7182 \r
7183 /*---------------------------------------------------------------------------*\\r
7184  *\r
7185  * Type-in move dialog functions\r
7186  * \r
7187 \*---------------------------------------------------------------------------*/\r
7188 \r
7189 LRESULT CALLBACK\r
7190 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7191 {\r
7192   char move[MSG_SIZ];\r
7193   HWND hInput;\r
7194   ChessMove moveType;\r
7195   int fromX, fromY, toX, toY;\r
7196   char promoChar;\r
7197 \r
7198   switch (message) {\r
7199   case WM_INITDIALOG:\r
7200     move[0] = (char) lParam;\r
7201     move[1] = NULLCHAR;\r
7202     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7203     hInput = GetDlgItem(hDlg, OPT_Move);\r
7204     SetWindowText(hInput, move);\r
7205     SetFocus(hInput);\r
7206     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7207     return FALSE;\r
7208 \r
7209   case WM_COMMAND:\r
7210     switch (LOWORD(wParam)) {\r
7211     case IDOK:\r
7212       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7213         gameMode != Training) {\r
7214         DisplayMoveError("Displayed move is not current");\r
7215       } else {\r
7216         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7217         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7218           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7219           if (gameMode != Training)\r
7220               forwardMostMove = currentMove;\r
7221           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7222         } else {\r
7223           DisplayMoveError("Could not parse move");\r
7224         }\r
7225       }\r
7226       EndDialog(hDlg, TRUE);\r
7227       return TRUE;\r
7228     case IDCANCEL:\r
7229       EndDialog(hDlg, FALSE);\r
7230       return TRUE;\r
7231     default:\r
7232       break;\r
7233     }\r
7234     break;\r
7235   }\r
7236   return FALSE;\r
7237 }\r
7238 \r
7239 VOID\r
7240 PopUpMoveDialog(char firstchar)\r
7241 {\r
7242     FARPROC lpProc;\r
7243     \r
7244     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7245         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7246         gameMode == AnalyzeMode || gameMode == EditGame || \r
7247         gameMode == EditPosition || gameMode == IcsExamining ||\r
7248         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7249         gameMode == Training) {\r
7250       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7251       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7252         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7253       FreeProcInstance(lpProc);\r
7254     }\r
7255 }\r
7256 \r
7257 /*---------------------------------------------------------------------------*\\r
7258  *\r
7259  * Type-in name dialog functions\r
7260  * \r
7261 \*---------------------------------------------------------------------------*/\r
7262 \r
7263 LRESULT CALLBACK\r
7264 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7265 {\r
7266   char move[MSG_SIZ];\r
7267   HWND hInput;\r
7268 \r
7269   switch (message) {\r
7270   case WM_INITDIALOG:\r
7271     move[0] = (char) lParam;\r
7272     move[1] = NULLCHAR;\r
7273     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7274     hInput = GetDlgItem(hDlg, OPT_Name);\r
7275     SetWindowText(hInput, move);\r
7276     SetFocus(hInput);\r
7277     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7278     return FALSE;\r
7279 \r
7280   case WM_COMMAND:\r
7281     switch (LOWORD(wParam)) {\r
7282     case IDOK:\r
7283       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7284       appData.userName = strdup(move);\r
7285 \r
7286       EndDialog(hDlg, TRUE);\r
7287       return TRUE;\r
7288     case IDCANCEL:\r
7289       EndDialog(hDlg, FALSE);\r
7290       return TRUE;\r
7291     default:\r
7292       break;\r
7293     }\r
7294     break;\r
7295   }\r
7296   return FALSE;\r
7297 }\r
7298 \r
7299 VOID\r
7300 PopUpNameDialog(char firstchar)\r
7301 {\r
7302     FARPROC lpProc;\r
7303     \r
7304       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7305       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7306         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7307       FreeProcInstance(lpProc);\r
7308 }\r
7309 \r
7310 /*---------------------------------------------------------------------------*\\r
7311  *\r
7312  *  Error dialogs\r
7313  * \r
7314 \*---------------------------------------------------------------------------*/\r
7315 \r
7316 /* Nonmodal error box */\r
7317 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7318                              WPARAM wParam, LPARAM lParam);\r
7319 \r
7320 VOID\r
7321 ErrorPopUp(char *title, char *content)\r
7322 {\r
7323   FARPROC lpProc;\r
7324   char *p, *q;\r
7325   BOOLEAN modal = hwndMain == NULL;\r
7326 \r
7327   p = content;\r
7328   q = errorMessage;\r
7329   while (*p) {\r
7330     if (*p == '\n') {\r
7331       if (modal) {\r
7332         *q++ = ' ';\r
7333         p++;\r
7334       } else {\r
7335         *q++ = '\r';\r
7336         *q++ = *p++;\r
7337       }\r
7338     } else {\r
7339       *q++ = *p++;\r
7340     }\r
7341   }\r
7342   *q = NULLCHAR;\r
7343   strncpy(errorTitle, title, sizeof(errorTitle));\r
7344   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7345   \r
7346   if (modal) {\r
7347     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7348   } else {\r
7349     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7350     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7351                  hwndMain, (DLGPROC)lpProc);\r
7352     FreeProcInstance(lpProc);\r
7353   }\r
7354 }\r
7355 \r
7356 VOID\r
7357 ErrorPopDown()\r
7358 {\r
7359   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7360   if (errorDialog == NULL) return;\r
7361   DestroyWindow(errorDialog);\r
7362   errorDialog = NULL;\r
7363 }\r
7364 \r
7365 LRESULT CALLBACK\r
7366 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7367 {\r
7368   HANDLE hwndText;\r
7369   RECT rChild;\r
7370 \r
7371   switch (message) {\r
7372   case WM_INITDIALOG:\r
7373     GetWindowRect(hDlg, &rChild);\r
7374 \r
7375     /*\r
7376     SetWindowPos(hDlg, NULL, rChild.left,\r
7377       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7378       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7379     */\r
7380 \r
7381     /* \r
7382         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7383         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7384         and it doesn't work when you resize the dialog.\r
7385         For now, just give it a default position.\r
7386     */\r
7387     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7388 \r
7389     errorDialog = hDlg;\r
7390     SetWindowText(hDlg, errorTitle);\r
7391     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7392     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7393     return FALSE;\r
7394 \r
7395   case WM_COMMAND:\r
7396     switch (LOWORD(wParam)) {\r
7397     case IDOK:\r
7398     case IDCANCEL:\r
7399       if (errorDialog == hDlg) errorDialog = NULL;\r
7400       DestroyWindow(hDlg);\r
7401       return TRUE;\r
7402 \r
7403     default:\r
7404       break;\r
7405     }\r
7406     break;\r
7407   }\r
7408   return FALSE;\r
7409 }\r
7410 \r
7411 #ifdef GOTHIC\r
7412 HWND gothicDialog = NULL;\r
7413 \r
7414 LRESULT CALLBACK\r
7415 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7416 {\r
7417   HANDLE hwndText;\r
7418   RECT rChild;\r
7419   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7420 \r
7421   switch (message) {\r
7422   case WM_INITDIALOG:\r
7423     GetWindowRect(hDlg, &rChild);\r
7424 \r
7425     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7426                                                              SWP_NOZORDER);\r
7427 \r
7428     /* \r
7429         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7430         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7431         and it doesn't work when you resize the dialog.\r
7432         For now, just give it a default position.\r
7433     */\r
7434     gothicDialog = hDlg;\r
7435     SetWindowText(hDlg, errorTitle);\r
7436     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7437     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7438     return FALSE;\r
7439 \r
7440   case WM_COMMAND:\r
7441     switch (LOWORD(wParam)) {\r
7442     case IDOK:\r
7443     case IDCANCEL:\r
7444       if (errorDialog == hDlg) errorDialog = NULL;\r
7445       DestroyWindow(hDlg);\r
7446       return TRUE;\r
7447 \r
7448     default:\r
7449       break;\r
7450     }\r
7451     break;\r
7452   }\r
7453   return FALSE;\r
7454 }\r
7455 \r
7456 VOID\r
7457 GothicPopUp(char *title, VariantClass variant)\r
7458 {\r
7459   FARPROC lpProc;\r
7460   static char *lastTitle;\r
7461 \r
7462   strncpy(errorTitle, title, sizeof(errorTitle));\r
7463   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7464 \r
7465   if(lastTitle != title && gothicDialog != NULL) {\r
7466     DestroyWindow(gothicDialog);\r
7467     gothicDialog = NULL;\r
7468   }\r
7469   if(variant != VariantNormal && gothicDialog == NULL) {\r
7470     title = lastTitle;\r
7471     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7472     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7473                  hwndMain, (DLGPROC)lpProc);\r
7474     FreeProcInstance(lpProc);\r
7475   }\r
7476 }\r
7477 #endif\r
7478 \r
7479 /*---------------------------------------------------------------------------*\\r
7480  *\r
7481  *  Ics Interaction console functions\r
7482  *\r
7483 \*---------------------------------------------------------------------------*/\r
7484 \r
7485 #define HISTORY_SIZE 64\r
7486 static char *history[HISTORY_SIZE];\r
7487 int histIn = 0, histP = 0;\r
7488 \r
7489 VOID\r
7490 SaveInHistory(char *cmd)\r
7491 {\r
7492   if (history[histIn] != NULL) {\r
7493     free(history[histIn]);\r
7494     history[histIn] = NULL;\r
7495   }\r
7496   if (*cmd == NULLCHAR) return;\r
7497   history[histIn] = StrSave(cmd);\r
7498   histIn = (histIn + 1) % HISTORY_SIZE;\r
7499   if (history[histIn] != NULL) {\r
7500     free(history[histIn]);\r
7501     history[histIn] = NULL;\r
7502   }\r
7503   histP = histIn;\r
7504 }\r
7505 \r
7506 char *\r
7507 PrevInHistory(char *cmd)\r
7508 {\r
7509   int newhp;\r
7510   if (histP == histIn) {\r
7511     if (history[histIn] != NULL) free(history[histIn]);\r
7512     history[histIn] = StrSave(cmd);\r
7513   }\r
7514   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7515   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7516   histP = newhp;\r
7517   return history[histP];\r
7518 }\r
7519 \r
7520 char *\r
7521 NextInHistory()\r
7522 {\r
7523   if (histP == histIn) return NULL;\r
7524   histP = (histP + 1) % HISTORY_SIZE;\r
7525   return history[histP];\r
7526 }\r
7527 \r
7528 typedef struct {\r
7529   char *item;\r
7530   char *command;\r
7531   BOOLEAN getname;\r
7532   BOOLEAN immediate;\r
7533 } IcsTextMenuEntry;\r
7534 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7535 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7536 \r
7537 void\r
7538 ParseIcsTextMenu(char *icsTextMenuString)\r
7539 {\r
7540 //  int flags = 0;\r
7541   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7542   char *p = icsTextMenuString;\r
7543   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7544     free(e->item);\r
7545     e->item = NULL;\r
7546     if (e->command != NULL) {\r
7547       free(e->command);\r
7548       e->command = NULL;\r
7549     }\r
7550     e++;\r
7551   }\r
7552   e = icsTextMenuEntry;\r
7553   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7554     if (*p == ';' || *p == '\n') {\r
7555       e->item = strdup("-");\r
7556       e->command = NULL;\r
7557       p++;\r
7558     } else if (*p == '-') {\r
7559       e->item = strdup("-");\r
7560       e->command = NULL;\r
7561       p++;\r
7562       if (*p) p++;\r
7563     } else {\r
7564       char *q, *r, *s, *t;\r
7565       char c;\r
7566       q = strchr(p, ',');\r
7567       if (q == NULL) break;\r
7568       *q = NULLCHAR;\r
7569       r = strchr(q + 1, ',');\r
7570       if (r == NULL) break;\r
7571       *r = NULLCHAR;\r
7572       s = strchr(r + 1, ',');\r
7573       if (s == NULL) break;\r
7574       *s = NULLCHAR;\r
7575       c = ';';\r
7576       t = strchr(s + 1, c);\r
7577       if (t == NULL) {\r
7578         c = '\n';\r
7579         t = strchr(s + 1, c);\r
7580       }\r
7581       if (t != NULL) *t = NULLCHAR;\r
7582       e->item = strdup(p);\r
7583       e->command = strdup(q + 1);\r
7584       e->getname = *(r + 1) != '0';\r
7585       e->immediate = *(s + 1) != '0';\r
7586       *q = ',';\r
7587       *r = ',';\r
7588       *s = ',';\r
7589       if (t == NULL) break;\r
7590       *t = c;\r
7591       p = t + 1;\r
7592     }\r
7593     e++;\r
7594   } \r
7595 }\r
7596 \r
7597 HMENU\r
7598 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7599 {\r
7600   HMENU hmenu, h;\r
7601   int i = 0;\r
7602   hmenu = LoadMenu(hInst, "TextMenu");\r
7603   h = GetSubMenu(hmenu, 0);\r
7604   while (e->item) {\r
7605     if (strcmp(e->item, "-") == 0) {\r
7606       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7607     } else {\r
7608       if (e->item[0] == '|') {\r
7609         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7610                    IDM_CommandX + i, &e->item[1]);\r
7611       } else {\r
7612         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7613       }\r
7614     }\r
7615     e++;\r
7616     i++;\r
7617   } \r
7618   return hmenu;\r
7619 }\r
7620 \r
7621 WNDPROC consoleTextWindowProc;\r
7622 \r
7623 void\r
7624 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7625 {\r
7626   char buf[MSG_SIZ], name[MSG_SIZ];\r
7627   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7628   CHARRANGE sel;\r
7629 \r
7630   if (!getname) {\r
7631     SetWindowText(hInput, command);\r
7632     if (immediate) {\r
7633       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7634     } else {\r
7635       sel.cpMin = 999999;\r
7636       sel.cpMax = 999999;\r
7637       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7638       SetFocus(hInput);\r
7639     }\r
7640     return;\r
7641   }    \r
7642   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7643   if (sel.cpMin == sel.cpMax) {\r
7644     /* Expand to surrounding word */\r
7645     TEXTRANGE tr;\r
7646     do {\r
7647       tr.chrg.cpMax = sel.cpMin;\r
7648       tr.chrg.cpMin = --sel.cpMin;\r
7649       if (sel.cpMin < 0) break;\r
7650       tr.lpstrText = name;\r
7651       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7652     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7653     sel.cpMin++;\r
7654 \r
7655     do {\r
7656       tr.chrg.cpMin = sel.cpMax;\r
7657       tr.chrg.cpMax = ++sel.cpMax;\r
7658       tr.lpstrText = name;\r
7659       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7660     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7661     sel.cpMax--;\r
7662 \r
7663     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7664       MessageBeep(MB_ICONEXCLAMATION);\r
7665       return;\r
7666     }\r
7667     tr.chrg = sel;\r
7668     tr.lpstrText = name;\r
7669     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7670   } else {\r
7671     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7672       MessageBeep(MB_ICONEXCLAMATION);\r
7673       return;\r
7674     }\r
7675     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7676   }\r
7677   if (immediate) {\r
7678     sprintf(buf, "%s %s", command, name);\r
7679     SetWindowText(hInput, buf);\r
7680     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7681   } else {\r
7682     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7683     SetWindowText(hInput, buf);\r
7684     sel.cpMin = 999999;\r
7685     sel.cpMax = 999999;\r
7686     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7687     SetFocus(hInput);\r
7688   }\r
7689 }\r
7690 \r
7691 LRESULT CALLBACK \r
7692 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7693 {\r
7694   HWND hInput;\r
7695   CHARRANGE sel;\r
7696 \r
7697   switch (message) {\r
7698   case WM_KEYDOWN:\r
7699     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7700     switch (wParam) {\r
7701     case VK_PRIOR:\r
7702       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7703       return 0;\r
7704     case VK_NEXT:\r
7705       sel.cpMin = 999999;\r
7706       sel.cpMax = 999999;\r
7707       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7708       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7709       return 0;\r
7710     }\r
7711     break;\r
7712   case WM_CHAR:\r
7713     if (wParam == '\t') {\r
7714       if (GetKeyState(VK_SHIFT) < 0) {\r
7715         /* shifted */\r
7716         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7717         if (buttonDesc[0].hwnd) {\r
7718           SetFocus(buttonDesc[0].hwnd);\r
7719         } else {\r
7720           SetFocus(hwndMain);\r
7721         }\r
7722       } else {\r
7723         /* unshifted */\r
7724         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7725       }\r
7726     } else {\r
7727       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7728       SetFocus(hInput);\r
7729       SendMessage(hInput, message, wParam, lParam);\r
7730     }\r
7731     return 0;\r
7732   case WM_PASTE:\r
7733     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7734     SetFocus(hInput);\r
7735     return SendMessage(hInput, message, wParam, lParam);\r
7736   case WM_MBUTTONDOWN:\r
7737     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7738   case WM_RBUTTONDOWN:\r
7739     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7740       /* Move selection here if it was empty */\r
7741       POINT pt;\r
7742       pt.x = LOWORD(lParam);\r
7743       pt.y = HIWORD(lParam);\r
7744       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7745       if (sel.cpMin == sel.cpMax) {\r
7746         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7747         sel.cpMax = sel.cpMin;\r
7748         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7749       }\r
7750       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7751     }\r
7752     return 0;\r
7753   case WM_RBUTTONUP:\r
7754     if (GetKeyState(VK_SHIFT) & ~1) {\r
7755       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7756         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7757     } else {\r
7758       POINT pt;\r
7759       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7760       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7761       if (sel.cpMin == sel.cpMax) {\r
7762         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7763         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7764       }\r
7765       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7766         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7767       }\r
7768       pt.x = LOWORD(lParam);\r
7769       pt.y = HIWORD(lParam);\r
7770       MenuPopup(hwnd, pt, hmenu, -1);\r
7771     }\r
7772     return 0;\r
7773   case WM_COMMAND:\r
7774     switch (LOWORD(wParam)) {\r
7775     case IDM_QuickPaste:\r
7776       {\r
7777         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7778         if (sel.cpMin == sel.cpMax) {\r
7779           MessageBeep(MB_ICONEXCLAMATION);\r
7780           return 0;\r
7781         }\r
7782         SendMessage(hwnd, WM_COPY, 0, 0);\r
7783         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7784         SendMessage(hInput, WM_PASTE, 0, 0);\r
7785         SetFocus(hInput);\r
7786         return 0;\r
7787       }\r
7788     case IDM_Cut:\r
7789       SendMessage(hwnd, WM_CUT, 0, 0);\r
7790       return 0;\r
7791     case IDM_Paste:\r
7792       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7793       return 0;\r
7794     case IDM_Copy:\r
7795       SendMessage(hwnd, WM_COPY, 0, 0);\r
7796       return 0;\r
7797     default:\r
7798       {\r
7799         int i = LOWORD(wParam) - IDM_CommandX;\r
7800         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7801             icsTextMenuEntry[i].command != NULL) {\r
7802           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7803                    icsTextMenuEntry[i].getname,\r
7804                    icsTextMenuEntry[i].immediate);\r
7805           return 0;\r
7806         }\r
7807       }\r
7808       break;\r
7809     }\r
7810     break;\r
7811   }\r
7812   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7813 }\r
7814 \r
7815 WNDPROC consoleInputWindowProc;\r
7816 \r
7817 LRESULT CALLBACK\r
7818 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7819 {\r
7820   char buf[MSG_SIZ];\r
7821   char *p;\r
7822   static BOOL sendNextChar = FALSE;\r
7823   static BOOL quoteNextChar = FALSE;\r
7824   InputSource *is = consoleInputSource;\r
7825   CHARFORMAT cf;\r
7826   CHARRANGE sel;\r
7827 \r
7828   switch (message) {\r
7829   case WM_CHAR:\r
7830     if (!appData.localLineEditing || sendNextChar) {\r
7831       is->buf[0] = (CHAR) wParam;\r
7832       is->count = 1;\r
7833       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7834       sendNextChar = FALSE;\r
7835       return 0;\r
7836     }\r
7837     if (quoteNextChar) {\r
7838       buf[0] = (char) wParam;\r
7839       buf[1] = NULLCHAR;\r
7840       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7841       quoteNextChar = FALSE;\r
7842       return 0;\r
7843     }\r
7844     switch (wParam) {\r
7845     case '\r':   /* Enter key */\r
7846       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7847       if (consoleEcho) SaveInHistory(is->buf);\r
7848       is->buf[is->count++] = '\n';\r
7849       is->buf[is->count] = NULLCHAR;\r
7850       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7851       if (consoleEcho) {\r
7852         ConsoleOutput(is->buf, is->count, TRUE);\r
7853       } else if (appData.localLineEditing) {\r
7854         ConsoleOutput("\n", 1, TRUE);\r
7855       }\r
7856       /* fall thru */\r
7857     case '\033': /* Escape key */\r
7858       SetWindowText(hwnd, "");\r
7859       cf.cbSize = sizeof(CHARFORMAT);\r
7860       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7861       if (consoleEcho) {\r
7862         cf.crTextColor = textAttribs[ColorNormal].color;\r
7863       } else {\r
7864         cf.crTextColor = COLOR_ECHOOFF;\r
7865       }\r
7866       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7867       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7868       return 0;\r
7869     case '\t':   /* Tab key */\r
7870       if (GetKeyState(VK_SHIFT) < 0) {\r
7871         /* shifted */\r
7872         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7873       } else {\r
7874         /* unshifted */\r
7875         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7876         if (buttonDesc[0].hwnd) {\r
7877           SetFocus(buttonDesc[0].hwnd);\r
7878         } else {\r
7879           SetFocus(hwndMain);\r
7880         }\r
7881       }\r
7882       return 0;\r
7883     case '\023': /* Ctrl+S */\r
7884       sendNextChar = TRUE;\r
7885       return 0;\r
7886     case '\021': /* Ctrl+Q */\r
7887       quoteNextChar = TRUE;\r
7888       return 0;\r
7889     default:\r
7890       break;\r
7891     }\r
7892     break;\r
7893   case WM_KEYDOWN:\r
7894     switch (wParam) {\r
7895     case VK_UP:\r
7896       GetWindowText(hwnd, buf, MSG_SIZ);\r
7897       p = PrevInHistory(buf);\r
7898       if (p != NULL) {\r
7899         SetWindowText(hwnd, p);\r
7900         sel.cpMin = 999999;\r
7901         sel.cpMax = 999999;\r
7902         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7903         return 0;\r
7904       }\r
7905       break;\r
7906     case VK_DOWN:\r
7907       p = NextInHistory();\r
7908       if (p != NULL) {\r
7909         SetWindowText(hwnd, p);\r
7910         sel.cpMin = 999999;\r
7911         sel.cpMax = 999999;\r
7912         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7913         return 0;\r
7914       }\r
7915       break;\r
7916     case VK_HOME:\r
7917     case VK_END:\r
7918       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7919       /* fall thru */\r
7920     case VK_PRIOR:\r
7921     case VK_NEXT:\r
7922       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7923       return 0;\r
7924     }\r
7925     break;\r
7926   case WM_MBUTTONDOWN:\r
7927     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7928       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7929     break;\r
7930   case WM_RBUTTONUP:\r
7931     if (GetKeyState(VK_SHIFT) & ~1) {\r
7932       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7933         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7934     } else {\r
7935       POINT pt;\r
7936       HMENU hmenu;\r
7937       hmenu = LoadMenu(hInst, "InputMenu");\r
7938       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7939       if (sel.cpMin == sel.cpMax) {\r
7940         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7941         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7942       }\r
7943       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7944         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7945       }\r
7946       pt.x = LOWORD(lParam);\r
7947       pt.y = HIWORD(lParam);\r
7948       MenuPopup(hwnd, pt, hmenu, -1);\r
7949     }\r
7950     return 0;\r
7951   case WM_COMMAND:\r
7952     switch (LOWORD(wParam)) { \r
7953     case IDM_Undo:\r
7954       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7955       return 0;\r
7956     case IDM_SelectAll:\r
7957       sel.cpMin = 0;\r
7958       sel.cpMax = -1; /*999999?*/\r
7959       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7960       return 0;\r
7961     case IDM_Cut:\r
7962       SendMessage(hwnd, WM_CUT, 0, 0);\r
7963       return 0;\r
7964     case IDM_Paste:\r
7965       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7966       return 0;\r
7967     case IDM_Copy:\r
7968       SendMessage(hwnd, WM_COPY, 0, 0);\r
7969       return 0;\r
7970     }\r
7971     break;\r
7972   }\r
7973   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7974 }\r
7975 \r
7976 #define CO_MAX  100000\r
7977 #define CO_TRIM   1000\r
7978 \r
7979 LRESULT CALLBACK\r
7980 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7981 {\r
7982   static SnapData sd;\r
7983   static HWND hText, hInput /*, hFocus*/;\r
7984 //  InputSource *is = consoleInputSource;\r
7985   RECT rect;\r
7986   static int sizeX, sizeY;\r
7987   int newSizeX, newSizeY;\r
7988   MINMAXINFO *mmi;\r
7989 \r
7990   switch (message) {\r
7991   case WM_INITDIALOG: /* message: initialize dialog box */\r
7992     hwndConsole = hDlg;\r
7993     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7994     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7995     SetFocus(hInput);\r
7996     consoleTextWindowProc = (WNDPROC)\r
7997       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7998     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7999     consoleInputWindowProc = (WNDPROC)\r
8000       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8001     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8002     Colorize(ColorNormal, TRUE);\r
8003     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8004     ChangedConsoleFont();\r
8005     GetClientRect(hDlg, &rect);\r
8006     sizeX = rect.right;\r
8007     sizeY = rect.bottom;\r
8008     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8009         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8010       WINDOWPLACEMENT wp;\r
8011       EnsureOnScreen(&consoleX, &consoleY);\r
8012       wp.length = sizeof(WINDOWPLACEMENT);\r
8013       wp.flags = 0;\r
8014       wp.showCmd = SW_SHOW;\r
8015       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8016       wp.rcNormalPosition.left = consoleX;\r
8017       wp.rcNormalPosition.right = consoleX + consoleW;\r
8018       wp.rcNormalPosition.top = consoleY;\r
8019       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8020       SetWindowPlacement(hDlg, &wp);\r
8021     }\r
8022 #if 0 \r
8023    // [HGM] Chessknight's change 2004-07-13\r
8024    else { /* Determine Defaults */\r
8025        WINDOWPLACEMENT wp;\r
8026        consoleX = winWidth + 1;\r
8027        consoleY = boardY;\r
8028        consoleW = screenWidth -  winWidth;\r
8029        consoleH = winHeight;\r
8030        EnsureOnScreen(&consoleX, &consoleY);\r
8031        wp.length = sizeof(WINDOWPLACEMENT);\r
8032        wp.flags = 0;\r
8033        wp.showCmd = SW_SHOW;\r
8034        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8035        wp.rcNormalPosition.left = consoleX;\r
8036        wp.rcNormalPosition.right = consoleX + consoleW;\r
8037        wp.rcNormalPosition.top = consoleY;\r
8038        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8039        SetWindowPlacement(hDlg, &wp);\r
8040     }\r
8041 #endif\r
8042     return FALSE;\r
8043 \r
8044   case WM_SETFOCUS:\r
8045     SetFocus(hInput);\r
8046     return 0;\r
8047 \r
8048   case WM_CLOSE:\r
8049     ExitEvent(0);\r
8050     /* not reached */\r
8051     break;\r
8052 \r
8053   case WM_SIZE:\r
8054     if (IsIconic(hDlg)) break;\r
8055     newSizeX = LOWORD(lParam);\r
8056     newSizeY = HIWORD(lParam);\r
8057     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8058       RECT rectText, rectInput;\r
8059       POINT pt;\r
8060       int newTextHeight, newTextWidth;\r
8061       GetWindowRect(hText, &rectText);\r
8062       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8063       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8064       if (newTextHeight < 0) {\r
8065         newSizeY += -newTextHeight;\r
8066         newTextHeight = 0;\r
8067       }\r
8068       SetWindowPos(hText, NULL, 0, 0,\r
8069         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8070       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8071       pt.x = rectInput.left;\r
8072       pt.y = rectInput.top + newSizeY - sizeY;\r
8073       ScreenToClient(hDlg, &pt);\r
8074       SetWindowPos(hInput, NULL, \r
8075         pt.x, pt.y, /* needs client coords */   \r
8076         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8077         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8078     }\r
8079     sizeX = newSizeX;\r
8080     sizeY = newSizeY;\r
8081     break;\r
8082 \r
8083   case WM_GETMINMAXINFO:\r
8084     /* Prevent resizing window too small */\r
8085     mmi = (MINMAXINFO *) lParam;\r
8086     mmi->ptMinTrackSize.x = 100;\r
8087     mmi->ptMinTrackSize.y = 100;\r
8088     break;\r
8089 \r
8090   /* [AS] Snapping */\r
8091   case WM_ENTERSIZEMOVE:\r
8092     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8093 \r
8094   case WM_SIZING:\r
8095     return OnSizing( &sd, hDlg, wParam, lParam );\r
8096 \r
8097   case WM_MOVING:\r
8098     return OnMoving( &sd, hDlg, wParam, lParam );\r
8099 \r
8100   case WM_EXITSIZEMOVE:\r
8101     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8102   }\r
8103 \r
8104   return DefWindowProc(hDlg, message, wParam, lParam);\r
8105 }\r
8106 \r
8107 \r
8108 VOID\r
8109 ConsoleCreate()\r
8110 {\r
8111   HWND hCons;\r
8112   if (hwndConsole) return;\r
8113   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8114   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8115 }\r
8116 \r
8117 \r
8118 VOID\r
8119 ConsoleOutput(char* data, int length, int forceVisible)\r
8120 {\r
8121   HWND hText;\r
8122   int trim, exlen;\r
8123   char *p, *q;\r
8124   char buf[CO_MAX+1];\r
8125   POINT pEnd;\r
8126   RECT rect;\r
8127   static int delayLF = 0;\r
8128   CHARRANGE savesel, sel;\r
8129 \r
8130   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8131   p = data;\r
8132   q = buf;\r
8133   if (delayLF) {\r
8134     *q++ = '\r';\r
8135     *q++ = '\n';\r
8136     delayLF = 0;\r
8137   }\r
8138   while (length--) {\r
8139     if (*p == '\n') {\r
8140       if (*++p) {\r
8141         *q++ = '\r';\r
8142         *q++ = '\n';\r
8143       } else {\r
8144         delayLF = 1;\r
8145       }\r
8146     } else if (*p == '\007') {\r
8147        MyPlaySound(&sounds[(int)SoundBell]);\r
8148        p++;\r
8149     } else {\r
8150       *q++ = *p++;\r
8151     }\r
8152   }\r
8153   *q = NULLCHAR;\r
8154   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8155   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8156   /* Save current selection */\r
8157   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8158   exlen = GetWindowTextLength(hText);\r
8159   /* Find out whether current end of text is visible */\r
8160   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8161   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8162   /* Trim existing text if it's too long */\r
8163   if (exlen + (q - buf) > CO_MAX) {\r
8164     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8165     sel.cpMin = 0;\r
8166     sel.cpMax = trim;\r
8167     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8168     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8169     exlen -= trim;\r
8170     savesel.cpMin -= trim;\r
8171     savesel.cpMax -= trim;\r
8172     if (exlen < 0) exlen = 0;\r
8173     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8174     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8175   }\r
8176   /* Append the new text */\r
8177   sel.cpMin = exlen;\r
8178   sel.cpMax = exlen;\r
8179   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8180   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8181   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8182   if (forceVisible || exlen == 0 ||\r
8183       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8184        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8185     /* Scroll to make new end of text visible if old end of text\r
8186        was visible or new text is an echo of user typein */\r
8187     sel.cpMin = 9999999;\r
8188     sel.cpMax = 9999999;\r
8189     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8190     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8191     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8192     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8193   }\r
8194   if (savesel.cpMax == exlen || forceVisible) {\r
8195     /* Move insert point to new end of text if it was at the old\r
8196        end of text or if the new text is an echo of user typein */\r
8197     sel.cpMin = 9999999;\r
8198     sel.cpMax = 9999999;\r
8199     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8200   } else {\r
8201     /* Restore previous selection */\r
8202     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8203   }\r
8204   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8205 }\r
8206 \r
8207 /*---------*/\r
8208 \r
8209 \r
8210 void\r
8211 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8212 {\r
8213   char buf[100];\r
8214   char *str;\r
8215   COLORREF oldFg, oldBg;\r
8216   HFONT oldFont;\r
8217   RECT rect;\r
8218 \r
8219   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8220 \r
8221   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8222   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8223   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8224 \r
8225   rect.left = x;\r
8226   rect.right = x + squareSize;\r
8227   rect.top  = y;\r
8228   rect.bottom = y + squareSize;\r
8229   str = buf;\r
8230 \r
8231   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8232                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8233              y, ETO_CLIPPED|ETO_OPAQUE,\r
8234              &rect, str, strlen(str), NULL);\r
8235 \r
8236   (void) SetTextColor(hdc, oldFg);\r
8237   (void) SetBkColor(hdc, oldBg);\r
8238   (void) SelectObject(hdc, oldFont);\r
8239 }\r
8240 \r
8241 void\r
8242 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8243               RECT *rect, char *color, char *flagFell)\r
8244 {\r
8245   char buf[100];\r
8246   char *str;\r
8247   COLORREF oldFg, oldBg;\r
8248   HFONT oldFont;\r
8249 \r
8250   if (appData.clockMode) {\r
8251     if (tinyLayout)\r
8252       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8253     else\r
8254       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8255     str = buf;\r
8256   } else {\r
8257     str = color;\r
8258   }\r
8259 \r
8260   if (highlight) {\r
8261     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8262     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8263   } else {\r
8264     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8265     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8266   }\r
8267   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8268 \r
8269   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8270              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8271              rect, str, strlen(str), NULL);\r
8272 \r
8273   (void) SetTextColor(hdc, oldFg);\r
8274   (void) SetBkColor(hdc, oldBg);\r
8275   (void) SelectObject(hdc, oldFont);\r
8276 }\r
8277 \r
8278 \r
8279 int\r
8280 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8281            OVERLAPPED *ovl)\r
8282 {\r
8283   int ok, err;\r
8284 \r
8285   /* [AS]  */\r
8286   if( count <= 0 ) {\r
8287     if (appData.debugMode) {\r
8288       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8289     }\r
8290 \r
8291     return ERROR_INVALID_USER_BUFFER;\r
8292   }\r
8293 \r
8294   ResetEvent(ovl->hEvent);\r
8295   ovl->Offset = ovl->OffsetHigh = 0;\r
8296   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8297   if (ok) {\r
8298     err = NO_ERROR;\r
8299   } else {\r
8300     err = GetLastError();\r
8301     if (err == ERROR_IO_PENDING) {\r
8302       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8303       if (ok)\r
8304         err = NO_ERROR;\r
8305       else\r
8306         err = GetLastError();\r
8307     }\r
8308   }\r
8309   return err;\r
8310 }\r
8311 \r
8312 int\r
8313 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8314             OVERLAPPED *ovl)\r
8315 {\r
8316   int ok, err;\r
8317 \r
8318   ResetEvent(ovl->hEvent);\r
8319   ovl->Offset = ovl->OffsetHigh = 0;\r
8320   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8321   if (ok) {\r
8322     err = NO_ERROR;\r
8323   } else {\r
8324     err = GetLastError();\r
8325     if (err == ERROR_IO_PENDING) {\r
8326       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8327       if (ok)\r
8328         err = NO_ERROR;\r
8329       else\r
8330         err = GetLastError();\r
8331     }\r
8332   }\r
8333   return err;\r
8334 }\r
8335 \r
8336 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8337 void CheckForInputBufferFull( InputSource * is )\r
8338 {\r
8339     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8340         /* Look for end of line */\r
8341         char * p = is->buf;\r
8342         \r
8343         while( p < is->next && *p != '\n' ) {\r
8344             p++;\r
8345         }\r
8346 \r
8347         if( p >= is->next ) {\r
8348             if (appData.debugMode) {\r
8349                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8350             }\r
8351 \r
8352             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8353             is->count = (DWORD) -1;\r
8354             is->next = is->buf;\r
8355         }\r
8356     }\r
8357 }\r
8358 \r
8359 DWORD\r
8360 InputThread(LPVOID arg)\r
8361 {\r
8362   InputSource *is;\r
8363   OVERLAPPED ovl;\r
8364 \r
8365   is = (InputSource *) arg;\r
8366   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8367   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8368   while (is->hThread != NULL) {\r
8369     is->error = DoReadFile(is->hFile, is->next,\r
8370                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8371                            &is->count, &ovl);\r
8372     if (is->error == NO_ERROR) {\r
8373       is->next += is->count;\r
8374     } else {\r
8375       if (is->error == ERROR_BROKEN_PIPE) {\r
8376         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8377         is->count = 0;\r
8378       } else {\r
8379         is->count = (DWORD) -1;\r
8380         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8381         break; \r
8382       }\r
8383     }\r
8384 \r
8385     CheckForInputBufferFull( is );\r
8386 \r
8387     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8388 \r
8389     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8390 \r
8391     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8392   }\r
8393 \r
8394   CloseHandle(ovl.hEvent);\r
8395   CloseHandle(is->hFile);\r
8396 \r
8397   if (appData.debugMode) {\r
8398     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8399   }\r
8400 \r
8401   return 0;\r
8402 }\r
8403 \r
8404 \r
8405 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8406 DWORD\r
8407 NonOvlInputThread(LPVOID arg)\r
8408 {\r
8409   InputSource *is;\r
8410   char *p, *q;\r
8411   int i;\r
8412   char prev;\r
8413 \r
8414   is = (InputSource *) arg;\r
8415   while (is->hThread != NULL) {\r
8416     is->error = ReadFile(is->hFile, is->next,\r
8417                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8418                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8419     if (is->error == NO_ERROR) {\r
8420       /* Change CRLF to LF */\r
8421       if (is->next > is->buf) {\r
8422         p = is->next - 1;\r
8423         i = is->count + 1;\r
8424       } else {\r
8425         p = is->next;\r
8426         i = is->count;\r
8427       }\r
8428       q = p;\r
8429       prev = NULLCHAR;\r
8430       while (i > 0) {\r
8431         if (prev == '\r' && *p == '\n') {\r
8432           *(q-1) = '\n';\r
8433           is->count--;\r
8434         } else { \r
8435           *q++ = *p;\r
8436         }\r
8437         prev = *p++;\r
8438         i--;\r
8439       }\r
8440       *q = NULLCHAR;\r
8441       is->next = q;\r
8442     } else {\r
8443       if (is->error == ERROR_BROKEN_PIPE) {\r
8444         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8445         is->count = 0; \r
8446       } else {\r
8447         is->count = (DWORD) -1;\r
8448       }\r
8449     }\r
8450 \r
8451     CheckForInputBufferFull( is );\r
8452 \r
8453     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8454 \r
8455     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8456 \r
8457     if (is->count < 0) break;  /* Quit on error */\r
8458   }\r
8459   CloseHandle(is->hFile);\r
8460   return 0;\r
8461 }\r
8462 \r
8463 DWORD\r
8464 SocketInputThread(LPVOID arg)\r
8465 {\r
8466   InputSource *is;\r
8467 \r
8468   is = (InputSource *) arg;\r
8469   while (is->hThread != NULL) {\r
8470     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8471     if ((int)is->count == SOCKET_ERROR) {\r
8472       is->count = (DWORD) -1;\r
8473       is->error = WSAGetLastError();\r
8474     } else {\r
8475       is->error = NO_ERROR;\r
8476       is->next += is->count;\r
8477       if (is->count == 0 && is->second == is) {\r
8478         /* End of file on stderr; quit with no message */\r
8479         break;\r
8480       }\r
8481     }\r
8482     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8483 \r
8484     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8485 \r
8486     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8487   }\r
8488   return 0;\r
8489 }\r
8490 \r
8491 VOID\r
8492 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8493 {\r
8494   InputSource *is;\r
8495 \r
8496   is = (InputSource *) lParam;\r
8497   if (is->lineByLine) {\r
8498     /* Feed in lines one by one */\r
8499     char *p = is->buf;\r
8500     char *q = p;\r
8501     while (q < is->next) {\r
8502       if (*q++ == '\n') {\r
8503         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8504         p = q;\r
8505       }\r
8506     }\r
8507     \r
8508     /* Move any partial line to the start of the buffer */\r
8509     q = is->buf;\r
8510     while (p < is->next) {\r
8511       *q++ = *p++;\r
8512     }\r
8513     is->next = q;\r
8514 \r
8515     if (is->error != NO_ERROR || is->count == 0) {\r
8516       /* Notify backend of the error.  Note: If there was a partial\r
8517          line at the end, it is not flushed through. */\r
8518       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8519     }\r
8520   } else {\r
8521     /* Feed in the whole chunk of input at once */\r
8522     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8523     is->next = is->buf;\r
8524   }\r
8525 }\r
8526 \r
8527 /*---------------------------------------------------------------------------*\\r
8528  *\r
8529  *  Menu enables. Used when setting various modes.\r
8530  *\r
8531 \*---------------------------------------------------------------------------*/\r
8532 \r
8533 typedef struct {\r
8534   int item;\r
8535   int flags;\r
8536 } Enables;\r
8537 \r
8538 VOID\r
8539 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8540 {\r
8541   while (enab->item > 0) {\r
8542     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8543     enab++;\r
8544   }\r
8545 }\r
8546 \r
8547 Enables gnuEnables[] = {\r
8548   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8558   { -1, -1 }\r
8559 };\r
8560 \r
8561 Enables icsEnables[] = {\r
8562   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8568   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8574   { -1, -1 }\r
8575 };\r
8576 \r
8577 #ifdef ZIPPY\r
8578 Enables zippyEnables[] = {\r
8579   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8580   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8581   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8582   { -1, -1 }\r
8583 };\r
8584 #endif\r
8585 \r
8586 Enables ncpEnables[] = {\r
8587   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8589   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8595   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8596   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8602   { -1, -1 }\r
8603 };\r
8604 \r
8605 Enables trainingOnEnables[] = {\r
8606   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8614   { -1, -1 }\r
8615 };\r
8616 \r
8617 Enables trainingOffEnables[] = {\r
8618   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8619   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8620   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8621   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8622   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8623   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8624   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8626   { -1, -1 }\r
8627 };\r
8628 \r
8629 /* These modify either ncpEnables or gnuEnables */\r
8630 Enables cmailEnables[] = {\r
8631   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8633   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8634   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8635   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8636   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8638   { -1, -1 }\r
8639 };\r
8640 \r
8641 Enables machineThinkingEnables[] = {\r
8642   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8652   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8657   { -1, -1 }\r
8658 };\r
8659 \r
8660 Enables userThinkingEnables[] = {\r
8661   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8663   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8664   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8665   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8666   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8667   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8668   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8670   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8671   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8672   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8675   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8676   { -1, -1 }\r
8677 };\r
8678 \r
8679 /*---------------------------------------------------------------------------*\\r
8680  *\r
8681  *  Front-end interface functions exported by XBoard.\r
8682  *  Functions appear in same order as prototypes in frontend.h.\r
8683  * \r
8684 \*---------------------------------------------------------------------------*/\r
8685 VOID\r
8686 ModeHighlight()\r
8687 {\r
8688   static UINT prevChecked = 0;\r
8689   static int prevPausing = 0;\r
8690   UINT nowChecked;\r
8691 \r
8692   if (pausing != prevPausing) {\r
8693     prevPausing = pausing;\r
8694     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8695                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8696     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8697   }\r
8698 \r
8699   switch (gameMode) {\r
8700   case BeginningOfGame:\r
8701     if (appData.icsActive)\r
8702       nowChecked = IDM_IcsClient;\r
8703     else if (appData.noChessProgram)\r
8704       nowChecked = IDM_EditGame;\r
8705     else\r
8706       nowChecked = IDM_MachineBlack;\r
8707     break;\r
8708   case MachinePlaysBlack:\r
8709     nowChecked = IDM_MachineBlack;\r
8710     break;\r
8711   case MachinePlaysWhite:\r
8712     nowChecked = IDM_MachineWhite;\r
8713     break;\r
8714   case TwoMachinesPlay:\r
8715     nowChecked = IDM_TwoMachines;\r
8716     break;\r
8717   case AnalyzeMode:\r
8718     nowChecked = IDM_AnalysisMode;\r
8719     break;\r
8720   case AnalyzeFile:\r
8721     nowChecked = IDM_AnalyzeFile;\r
8722     break;\r
8723   case EditGame:\r
8724     nowChecked = IDM_EditGame;\r
8725     break;\r
8726   case PlayFromGameFile:\r
8727     nowChecked = IDM_LoadGame;\r
8728     break;\r
8729   case EditPosition:\r
8730     nowChecked = IDM_EditPosition;\r
8731     break;\r
8732   case Training:\r
8733     nowChecked = IDM_Training;\r
8734     break;\r
8735   case IcsPlayingWhite:\r
8736   case IcsPlayingBlack:\r
8737   case IcsObserving:\r
8738   case IcsIdle:\r
8739     nowChecked = IDM_IcsClient;\r
8740     break;\r
8741   default:\r
8742   case EndOfGame:\r
8743     nowChecked = 0;\r
8744     break;\r
8745   }\r
8746   if (prevChecked != 0)\r
8747     (void) CheckMenuItem(GetMenu(hwndMain),\r
8748                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8749   if (nowChecked != 0)\r
8750     (void) CheckMenuItem(GetMenu(hwndMain),\r
8751                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8752 \r
8753   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8754     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8755                           MF_BYCOMMAND|MF_ENABLED);\r
8756   } else {\r
8757     (void) EnableMenuItem(GetMenu(hwndMain), \r
8758                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8759   }\r
8760 \r
8761   prevChecked = nowChecked;\r
8762 \r
8763   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8764   if (appData.icsActive) {\r
8765        if (appData.icsEngineAnalyze) {\r
8766                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8767                        MF_BYCOMMAND|MF_CHECKED);\r
8768        } else {\r
8769                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8770                        MF_BYCOMMAND|MF_UNCHECKED);\r
8771        }\r
8772   }\r
8773 }\r
8774 \r
8775 VOID\r
8776 SetICSMode()\r
8777 {\r
8778   HMENU hmenu = GetMenu(hwndMain);\r
8779   SetMenuEnables(hmenu, icsEnables);\r
8780   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8781     MF_BYPOSITION|MF_ENABLED);\r
8782 #ifdef ZIPPY\r
8783   if (appData.zippyPlay) {\r
8784     SetMenuEnables(hmenu, zippyEnables);\r
8785     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8786          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8787           MF_BYCOMMAND|MF_ENABLED);\r
8788   }\r
8789 #endif\r
8790 }\r
8791 \r
8792 VOID\r
8793 SetGNUMode()\r
8794 {\r
8795   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8796 }\r
8797 \r
8798 VOID\r
8799 SetNCPMode()\r
8800 {\r
8801   HMENU hmenu = GetMenu(hwndMain);\r
8802   SetMenuEnables(hmenu, ncpEnables);\r
8803   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8804     MF_BYPOSITION|MF_GRAYED);\r
8805     DrawMenuBar(hwndMain);\r
8806 }\r
8807 \r
8808 VOID\r
8809 SetCmailMode()\r
8810 {\r
8811   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8812 }\r
8813 \r
8814 VOID \r
8815 SetTrainingModeOn()\r
8816 {\r
8817   int i;\r
8818   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8819   for (i = 0; i < N_BUTTONS; i++) {\r
8820     if (buttonDesc[i].hwnd != NULL)\r
8821       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8822   }\r
8823   CommentPopDown();\r
8824 }\r
8825 \r
8826 VOID SetTrainingModeOff()\r
8827 {\r
8828   int i;\r
8829   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8830   for (i = 0; i < N_BUTTONS; i++) {\r
8831     if (buttonDesc[i].hwnd != NULL)\r
8832       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8833   }\r
8834 }\r
8835 \r
8836 \r
8837 VOID\r
8838 SetUserThinkingEnables()\r
8839 {\r
8840   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8841 }\r
8842 \r
8843 VOID\r
8844 SetMachineThinkingEnables()\r
8845 {\r
8846   HMENU hMenu = GetMenu(hwndMain);\r
8847   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8848 \r
8849   SetMenuEnables(hMenu, machineThinkingEnables);\r
8850 \r
8851   if (gameMode == MachinePlaysBlack) {\r
8852     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8853   } else if (gameMode == MachinePlaysWhite) {\r
8854     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8855   } else if (gameMode == TwoMachinesPlay) {\r
8856     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8857   }\r
8858 }\r
8859 \r
8860 \r
8861 VOID\r
8862 DisplayTitle(char *str)\r
8863 {\r
8864   char title[MSG_SIZ], *host;\r
8865   if (str[0] != NULLCHAR) {\r
8866     strcpy(title, str);\r
8867   } else if (appData.icsActive) {\r
8868     if (appData.icsCommPort[0] != NULLCHAR)\r
8869       host = "ICS";\r
8870     else \r
8871       host = appData.icsHost;\r
8872     sprintf(title, "%s: %s", szTitle, host);\r
8873   } else if (appData.noChessProgram) {\r
8874     strcpy(title, szTitle);\r
8875   } else {\r
8876     strcpy(title, szTitle);\r
8877     strcat(title, ": ");\r
8878     strcat(title, first.tidy);\r
8879   }\r
8880   SetWindowText(hwndMain, title);\r
8881 }\r
8882 \r
8883 \r
8884 VOID\r
8885 DisplayMessage(char *str1, char *str2)\r
8886 {\r
8887   HDC hdc;\r
8888   HFONT oldFont;\r
8889   int remain = MESSAGE_TEXT_MAX - 1;\r
8890   int len;\r
8891 \r
8892   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8893   messageText[0] = NULLCHAR;\r
8894   if (*str1) {\r
8895     len = strlen(str1);\r
8896     if (len > remain) len = remain;\r
8897     strncpy(messageText, str1, len);\r
8898     messageText[len] = NULLCHAR;\r
8899     remain -= len;\r
8900   }\r
8901   if (*str2 && remain >= 2) {\r
8902     if (*str1) {\r
8903       strcat(messageText, "  ");\r
8904       remain -= 2;\r
8905     }\r
8906     len = strlen(str2);\r
8907     if (len > remain) len = remain;\r
8908     strncat(messageText, str2, len);\r
8909   }\r
8910   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8911 \r
8912   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8913   hdc = GetDC(hwndMain);\r
8914   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8915   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8916              &messageRect, messageText, strlen(messageText), NULL);\r
8917   (void) SelectObject(hdc, oldFont);\r
8918   (void) ReleaseDC(hwndMain, hdc);\r
8919 }\r
8920 \r
8921 VOID\r
8922 DisplayError(char *str, int error)\r
8923 {\r
8924   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8925   int len;\r
8926 \r
8927   if (error == 0) {\r
8928     strcpy(buf, str);\r
8929   } else {\r
8930     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8931                         NULL, error, LANG_NEUTRAL,\r
8932                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8933     if (len > 0) {\r
8934       sprintf(buf, "%s:\n%s", str, buf2);\r
8935     } else {\r
8936       ErrorMap *em = errmap;\r
8937       while (em->err != 0 && em->err != error) em++;\r
8938       if (em->err != 0) {\r
8939         sprintf(buf, "%s:\n%s", str, em->msg);\r
8940       } else {\r
8941         sprintf(buf, "%s:\nError code %d", str, error);\r
8942       }\r
8943     }\r
8944   }\r
8945   \r
8946   ErrorPopUp("Error", buf);\r
8947 }\r
8948 \r
8949 \r
8950 VOID\r
8951 DisplayMoveError(char *str)\r
8952 {\r
8953   fromX = fromY = -1;\r
8954   ClearHighlights();\r
8955   DrawPosition(FALSE, NULL);\r
8956   if (appData.popupMoveErrors) {\r
8957     ErrorPopUp("Error", str);\r
8958   } else {\r
8959     DisplayMessage(str, "");\r
8960     moveErrorMessageUp = TRUE;\r
8961   }\r
8962 }\r
8963 \r
8964 VOID\r
8965 DisplayFatalError(char *str, int error, int exitStatus)\r
8966 {\r
8967   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8968   int len;\r
8969   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8970 \r
8971   if (error != 0) {\r
8972     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8973                         NULL, error, LANG_NEUTRAL,\r
8974                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8975     if (len > 0) {\r
8976       sprintf(buf, "%s:\n%s", str, buf2);\r
8977     } else {\r
8978       ErrorMap *em = errmap;\r
8979       while (em->err != 0 && em->err != error) em++;\r
8980       if (em->err != 0) {\r
8981         sprintf(buf, "%s:\n%s", str, em->msg);\r
8982       } else {\r
8983         sprintf(buf, "%s:\nError code %d", str, error);\r
8984       }\r
8985     }\r
8986     str = buf;\r
8987   }\r
8988   if (appData.debugMode) {\r
8989     fprintf(debugFP, "%s: %s\n", label, str);\r
8990   }\r
8991   if (appData.popupExitMessage) {\r
8992     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8993                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8994   }\r
8995   ExitEvent(exitStatus);\r
8996 }\r
8997 \r
8998 \r
8999 VOID\r
9000 DisplayInformation(char *str)\r
9001 {\r
9002   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9003 }\r
9004 \r
9005 \r
9006 VOID\r
9007 DisplayNote(char *str)\r
9008 {\r
9009   ErrorPopUp("Note", str);\r
9010 }\r
9011 \r
9012 \r
9013 typedef struct {\r
9014   char *title, *question, *replyPrefix;\r
9015   ProcRef pr;\r
9016 } QuestionParams;\r
9017 \r
9018 LRESULT CALLBACK\r
9019 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9020 {\r
9021   static QuestionParams *qp;\r
9022   char reply[MSG_SIZ];\r
9023   int len, err;\r
9024 \r
9025   switch (message) {\r
9026   case WM_INITDIALOG:\r
9027     qp = (QuestionParams *) lParam;\r
9028     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9029     SetWindowText(hDlg, qp->title);\r
9030     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9031     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9032     return FALSE;\r
9033 \r
9034   case WM_COMMAND:\r
9035     switch (LOWORD(wParam)) {\r
9036     case IDOK:\r
9037       strcpy(reply, qp->replyPrefix);\r
9038       if (*reply) strcat(reply, " ");\r
9039       len = strlen(reply);\r
9040       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9041       strcat(reply, "\n");\r
9042       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9043       EndDialog(hDlg, TRUE);\r
9044       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9045       return TRUE;\r
9046     case IDCANCEL:\r
9047       EndDialog(hDlg, FALSE);\r
9048       return TRUE;\r
9049     default:\r
9050       break;\r
9051     }\r
9052     break;\r
9053   }\r
9054   return FALSE;\r
9055 }\r
9056 \r
9057 VOID\r
9058 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9059 {\r
9060     QuestionParams qp;\r
9061     FARPROC lpProc;\r
9062     \r
9063     qp.title = title;\r
9064     qp.question = question;\r
9065     qp.replyPrefix = replyPrefix;\r
9066     qp.pr = pr;\r
9067     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9068     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9069       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9070     FreeProcInstance(lpProc);\r
9071 }\r
9072 \r
9073 /* [AS] Pick FRC position */\r
9074 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9075 {\r
9076     static int * lpIndexFRC;\r
9077     BOOL index_is_ok;\r
9078     char buf[16];\r
9079 \r
9080     switch( message )\r
9081     {\r
9082     case WM_INITDIALOG:\r
9083         lpIndexFRC = (int *) lParam;\r
9084 \r
9085         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9086 \r
9087         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9088         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9089         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9090         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9091 \r
9092         break;\r
9093 \r
9094     case WM_COMMAND:\r
9095         switch( LOWORD(wParam) ) {\r
9096         case IDOK:\r
9097             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9098             EndDialog( hDlg, 0 );\r
9099             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9100             return TRUE;\r
9101         case IDCANCEL:\r
9102             EndDialog( hDlg, 1 );   \r
9103             return TRUE;\r
9104         case IDC_NFG_Edit:\r
9105             if( HIWORD(wParam) == EN_CHANGE ) {\r
9106                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9107 \r
9108                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9109             }\r
9110             return TRUE;\r
9111         case IDC_NFG_Random:\r
9112             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9113             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9114             return TRUE;\r
9115         }\r
9116 \r
9117         break;\r
9118     }\r
9119 \r
9120     return FALSE;\r
9121 }\r
9122 \r
9123 int NewGameFRC()\r
9124 {\r
9125     int result;\r
9126     int index = appData.defaultFrcPosition;\r
9127     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9128 \r
9129     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9130 \r
9131     if( result == 0 ) {\r
9132         appData.defaultFrcPosition = index;\r
9133     }\r
9134 \r
9135     return result;\r
9136 }\r
9137 \r
9138 /* [AS] Game list options */\r
9139 typedef struct {\r
9140     char id;\r
9141     char * name;\r
9142 } GLT_Item;\r
9143 \r
9144 static GLT_Item GLT_ItemInfo[] = {\r
9145     { GLT_EVENT,      "Event" },\r
9146     { GLT_SITE,       "Site" },\r
9147     { GLT_DATE,       "Date" },\r
9148     { GLT_ROUND,      "Round" },\r
9149     { GLT_PLAYERS,    "Players" },\r
9150     { GLT_RESULT,     "Result" },\r
9151     { GLT_WHITE_ELO,  "White Rating" },\r
9152     { GLT_BLACK_ELO,  "Black Rating" },\r
9153     { GLT_TIME_CONTROL,"Time Control" },\r
9154     { GLT_VARIANT,    "Variant" },\r
9155     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9156     { 0, 0 }\r
9157 };\r
9158 \r
9159 const char * GLT_FindItem( char id )\r
9160 {\r
9161     const char * result = 0;\r
9162 \r
9163     GLT_Item * list = GLT_ItemInfo;\r
9164 \r
9165     while( list->id != 0 ) {\r
9166         if( list->id == id ) {\r
9167             result = list->name;\r
9168             break;\r
9169         }\r
9170 \r
9171         list++;\r
9172     }\r
9173 \r
9174     return result;\r
9175 }\r
9176 \r
9177 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9178 {\r
9179     const char * name = GLT_FindItem( id );\r
9180 \r
9181     if( name != 0 ) {\r
9182         if( index >= 0 ) {\r
9183             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9184         }\r
9185         else {\r
9186             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9187         }\r
9188     }\r
9189 }\r
9190 \r
9191 void GLT_TagsToList( HWND hDlg, char * tags )\r
9192 {\r
9193     char * pc = tags;\r
9194 \r
9195     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9196 \r
9197     while( *pc ) {\r
9198         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9199         pc++;\r
9200     }\r
9201 \r
9202     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9203 \r
9204     pc = GLT_ALL_TAGS;\r
9205 \r
9206     while( *pc ) {\r
9207         if( strchr( tags, *pc ) == 0 ) {\r
9208             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9209         }\r
9210         pc++;\r
9211     }\r
9212 \r
9213     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9214 }\r
9215 \r
9216 char GLT_ListItemToTag( HWND hDlg, int index )\r
9217 {\r
9218     char result = '\0';\r
9219     char name[128];\r
9220 \r
9221     GLT_Item * list = GLT_ItemInfo;\r
9222 \r
9223     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9224         while( list->id != 0 ) {\r
9225             if( strcmp( list->name, name ) == 0 ) {\r
9226                 result = list->id;\r
9227                 break;\r
9228             }\r
9229 \r
9230             list++;\r
9231         }\r
9232     }\r
9233 \r
9234     return result;\r
9235 }\r
9236 \r
9237 void GLT_MoveSelection( HWND hDlg, int delta )\r
9238 {\r
9239     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9240     int idx2 = idx1 + delta;\r
9241     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9242 \r
9243     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9244         char buf[128];\r
9245 \r
9246         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9247         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9248         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9249         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9250     }\r
9251 }\r
9252 \r
9253 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9254 {\r
9255     static char glt[64];\r
9256     static char * lpUserGLT;\r
9257 \r
9258     switch( message )\r
9259     {\r
9260     case WM_INITDIALOG:\r
9261         lpUserGLT = (char *) lParam;\r
9262         \r
9263         strcpy( glt, lpUserGLT );\r
9264 \r
9265         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9266 \r
9267         /* Initialize list */\r
9268         GLT_TagsToList( hDlg, glt );\r
9269 \r
9270         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9271 \r
9272         break;\r
9273 \r
9274     case WM_COMMAND:\r
9275         switch( LOWORD(wParam) ) {\r
9276         case IDOK:\r
9277             {\r
9278                 char * pc = lpUserGLT;\r
9279                 int idx = 0;\r
9280 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9281                 char id;\r
9282 \r
9283                 do {\r
9284                     id = GLT_ListItemToTag( hDlg, idx );\r
9285 \r
9286                     *pc++ = id;\r
9287                     idx++;\r
9288                 } while( id != '\0' );\r
9289             }\r
9290             EndDialog( hDlg, 0 );\r
9291             return TRUE;\r
9292         case IDCANCEL:\r
9293             EndDialog( hDlg, 1 );\r
9294             return TRUE;\r
9295 \r
9296         case IDC_GLT_Default:\r
9297             strcpy( glt, GLT_DEFAULT_TAGS );\r
9298             GLT_TagsToList( hDlg, glt );\r
9299             return TRUE;\r
9300 \r
9301         case IDC_GLT_Restore:\r
9302             strcpy( glt, lpUserGLT );\r
9303             GLT_TagsToList( hDlg, glt );\r
9304             return TRUE;\r
9305 \r
9306         case IDC_GLT_Up:\r
9307             GLT_MoveSelection( hDlg, -1 );\r
9308             return TRUE;\r
9309 \r
9310         case IDC_GLT_Down:\r
9311             GLT_MoveSelection( hDlg, +1 );\r
9312             return TRUE;\r
9313         }\r
9314 \r
9315         break;\r
9316     }\r
9317 \r
9318     return FALSE;\r
9319 }\r
9320 \r
9321 int GameListOptions()\r
9322 {\r
9323     char glt[64];\r
9324     int result;\r
9325     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9326 \r
9327     strcpy( glt, appData.gameListTags );\r
9328 \r
9329     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9330 \r
9331     if( result == 0 ) {\r
9332         /* [AS] Memory leak here! */\r
9333         appData.gameListTags = strdup( glt ); \r
9334     }\r
9335 \r
9336     return result;\r
9337 }\r
9338 \r
9339 \r
9340 VOID\r
9341 DisplayIcsInteractionTitle(char *str)\r
9342 {\r
9343   char consoleTitle[MSG_SIZ];\r
9344 \r
9345   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9346   SetWindowText(hwndConsole, consoleTitle);\r
9347 }\r
9348 \r
9349 void\r
9350 DrawPosition(int fullRedraw, Board board)\r
9351 {\r
9352   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9353 }\r
9354 \r
9355 \r
9356 VOID\r
9357 ResetFrontEnd()\r
9358 {\r
9359   fromX = fromY = -1;\r
9360   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9361     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9362     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9363     dragInfo.lastpos = dragInfo.pos;\r
9364     dragInfo.start.x = dragInfo.start.y = -1;\r
9365     dragInfo.from = dragInfo.start;\r
9366     ReleaseCapture();\r
9367     DrawPosition(TRUE, NULL);\r
9368   }\r
9369 }\r
9370 \r
9371 \r
9372 VOID\r
9373 CommentPopUp(char *title, char *str)\r
9374 {\r
9375   HWND hwnd = GetActiveWindow();\r
9376   EitherCommentPopUp(0, title, str, FALSE);\r
9377   SetActiveWindow(hwnd);\r
9378 }\r
9379 \r
9380 VOID\r
9381 CommentPopDown(void)\r
9382 {\r
9383   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9384   if (commentDialog) {\r
9385     ShowWindow(commentDialog, SW_HIDE);\r
9386   }\r
9387   commentDialogUp = FALSE;\r
9388 }\r
9389 \r
9390 VOID\r
9391 EditCommentPopUp(int index, char *title, char *str)\r
9392 {\r
9393   EitherCommentPopUp(index, title, str, TRUE);\r
9394 }\r
9395 \r
9396 \r
9397 VOID\r
9398 RingBell()\r
9399 {\r
9400   MyPlaySound(&sounds[(int)SoundMove]);\r
9401 }\r
9402 \r
9403 VOID PlayIcsWinSound()\r
9404 {\r
9405   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9406 }\r
9407 \r
9408 VOID PlayIcsLossSound()\r
9409 {\r
9410   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9411 }\r
9412 \r
9413 VOID PlayIcsDrawSound()\r
9414 {\r
9415   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9416 }\r
9417 \r
9418 VOID PlayIcsUnfinishedSound()\r
9419 {\r
9420   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9421 }\r
9422 \r
9423 VOID\r
9424 PlayAlarmSound()\r
9425 {\r
9426   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9427 }\r
9428 \r
9429 \r
9430 VOID\r
9431 EchoOn()\r
9432 {\r
9433   HWND hInput;\r
9434   consoleEcho = TRUE;\r
9435   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9436   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9437   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9438 }\r
9439 \r
9440 \r
9441 VOID\r
9442 EchoOff()\r
9443 {\r
9444   CHARFORMAT cf;\r
9445   HWND hInput;\r
9446   consoleEcho = FALSE;\r
9447   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9448   /* This works OK: set text and background both to the same color */\r
9449   cf = consoleCF;\r
9450   cf.crTextColor = COLOR_ECHOOFF;\r
9451   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9452   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9453 }\r
9454 \r
9455 /* No Raw()...? */\r
9456 \r
9457 void Colorize(ColorClass cc, int continuation)\r
9458 {\r
9459   currentColorClass = cc;\r
9460   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9461   consoleCF.crTextColor = textAttribs[cc].color;\r
9462   consoleCF.dwEffects = textAttribs[cc].effects;\r
9463   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9464 }\r
9465 \r
9466 char *\r
9467 UserName()\r
9468 {\r
9469   static char buf[MSG_SIZ];\r
9470   DWORD bufsiz = MSG_SIZ;\r
9471 \r
9472   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9473         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9474   }\r
9475   if (!GetUserName(buf, &bufsiz)) {\r
9476     /*DisplayError("Error getting user name", GetLastError());*/\r
9477     strcpy(buf, "User");\r
9478   }\r
9479   return buf;\r
9480 }\r
9481 \r
9482 char *\r
9483 HostName()\r
9484 {\r
9485   static char buf[MSG_SIZ];\r
9486   DWORD bufsiz = MSG_SIZ;\r
9487 \r
9488   if (!GetComputerName(buf, &bufsiz)) {\r
9489     /*DisplayError("Error getting host name", GetLastError());*/\r
9490     strcpy(buf, "Unknown");\r
9491   }\r
9492   return buf;\r
9493 }\r
9494 \r
9495 \r
9496 int\r
9497 ClockTimerRunning()\r
9498 {\r
9499   return clockTimerEvent != 0;\r
9500 }\r
9501 \r
9502 int\r
9503 StopClockTimer()\r
9504 {\r
9505   if (clockTimerEvent == 0) return FALSE;\r
9506   KillTimer(hwndMain, clockTimerEvent);\r
9507   clockTimerEvent = 0;\r
9508   return TRUE;\r
9509 }\r
9510 \r
9511 void\r
9512 StartClockTimer(long millisec)\r
9513 {\r
9514   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9515                              (UINT) millisec, NULL);\r
9516 }\r
9517 \r
9518 void\r
9519 DisplayWhiteClock(long timeRemaining, int highlight)\r
9520 {\r
9521   HDC hdc;\r
9522   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9523 \r
9524   if(appData.noGUI) return;\r
9525   hdc = GetDC(hwndMain);\r
9526   if (!IsIconic(hwndMain)) {\r
9527     DisplayAClock(hdc, timeRemaining, highlight, \r
9528                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9529   }\r
9530   if (highlight && iconCurrent == iconBlack) {\r
9531     iconCurrent = iconWhite;\r
9532     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9533     if (IsIconic(hwndMain)) {\r
9534       DrawIcon(hdc, 2, 2, iconCurrent);\r
9535     }\r
9536   }\r
9537   (void) ReleaseDC(hwndMain, hdc);\r
9538   if (hwndConsole)\r
9539     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9540 }\r
9541 \r
9542 void\r
9543 DisplayBlackClock(long timeRemaining, int highlight)\r
9544 {\r
9545   HDC hdc;\r
9546   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9547 \r
9548   if(appData.noGUI) return;\r
9549   hdc = GetDC(hwndMain);\r
9550   if (!IsIconic(hwndMain)) {\r
9551     DisplayAClock(hdc, timeRemaining, highlight, \r
9552                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9553   }\r
9554   if (highlight && iconCurrent == iconWhite) {\r
9555     iconCurrent = iconBlack;\r
9556     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9557     if (IsIconic(hwndMain)) {\r
9558       DrawIcon(hdc, 2, 2, iconCurrent);\r
9559     }\r
9560   }\r
9561   (void) ReleaseDC(hwndMain, hdc);\r
9562   if (hwndConsole)\r
9563     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9564 }\r
9565 \r
9566 \r
9567 int\r
9568 LoadGameTimerRunning()\r
9569 {\r
9570   return loadGameTimerEvent != 0;\r
9571 }\r
9572 \r
9573 int\r
9574 StopLoadGameTimer()\r
9575 {\r
9576   if (loadGameTimerEvent == 0) return FALSE;\r
9577   KillTimer(hwndMain, loadGameTimerEvent);\r
9578   loadGameTimerEvent = 0;\r
9579   return TRUE;\r
9580 }\r
9581 \r
9582 void\r
9583 StartLoadGameTimer(long millisec)\r
9584 {\r
9585   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9586                                 (UINT) millisec, NULL);\r
9587 }\r
9588 \r
9589 void\r
9590 AutoSaveGame()\r
9591 {\r
9592   char *defName;\r
9593   FILE *f;\r
9594   char fileTitle[MSG_SIZ];\r
9595 \r
9596   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9597   f = OpenFileDialog(hwndMain, "a", defName,\r
9598                      appData.oldSaveStyle ? "gam" : "pgn",\r
9599                      GAME_FILT, \r
9600                      "Save Game to File", NULL, fileTitle, NULL);\r
9601   if (f != NULL) {\r
9602     SaveGame(f, 0, "");\r
9603     fclose(f);\r
9604   }\r
9605 }\r
9606 \r
9607 \r
9608 void\r
9609 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9610 {\r
9611   if (delayedTimerEvent != 0) {\r
9612     if (appData.debugMode) {\r
9613       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9614     }\r
9615     KillTimer(hwndMain, delayedTimerEvent);\r
9616     delayedTimerEvent = 0;\r
9617     delayedTimerCallback();\r
9618   }\r
9619   delayedTimerCallback = cb;\r
9620   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9621                                 (UINT) millisec, NULL);\r
9622 }\r
9623 \r
9624 DelayedEventCallback\r
9625 GetDelayedEvent()\r
9626 {\r
9627   if (delayedTimerEvent) {\r
9628     return delayedTimerCallback;\r
9629   } else {\r
9630     return NULL;\r
9631   }\r
9632 }\r
9633 \r
9634 void\r
9635 CancelDelayedEvent()\r
9636 {\r
9637   if (delayedTimerEvent) {\r
9638     KillTimer(hwndMain, delayedTimerEvent);\r
9639     delayedTimerEvent = 0;\r
9640   }\r
9641 }\r
9642 \r
9643 DWORD GetWin32Priority(int nice)\r
9644 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9645 /*\r
9646 REALTIME_PRIORITY_CLASS     0x00000100\r
9647 HIGH_PRIORITY_CLASS         0x00000080\r
9648 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9649 NORMAL_PRIORITY_CLASS       0x00000020\r
9650 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9651 IDLE_PRIORITY_CLASS         0x00000040\r
9652 */\r
9653         if (nice < -15) return 0x00000080;\r
9654         if (nice < 0)   return 0x00008000;\r
9655         if (nice == 0)  return 0x00000020;\r
9656         if (nice < 15)  return 0x00004000;\r
9657         return 0x00000040;\r
9658 }\r
9659 \r
9660 /* Start a child process running the given program.\r
9661    The process's standard output can be read from "from", and its\r
9662    standard input can be written to "to".\r
9663    Exit with fatal error if anything goes wrong.\r
9664    Returns an opaque pointer that can be used to destroy the process\r
9665    later.\r
9666 */\r
9667 int\r
9668 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9669 {\r
9670 #define BUFSIZE 4096\r
9671 \r
9672   HANDLE hChildStdinRd, hChildStdinWr,\r
9673     hChildStdoutRd, hChildStdoutWr;\r
9674   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9675   SECURITY_ATTRIBUTES saAttr;\r
9676   BOOL fSuccess;\r
9677   PROCESS_INFORMATION piProcInfo;\r
9678   STARTUPINFO siStartInfo;\r
9679   ChildProc *cp;\r
9680   char buf[MSG_SIZ];\r
9681   DWORD err;\r
9682 \r
9683   if (appData.debugMode) {\r
9684     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9685   }\r
9686 \r
9687   *pr = NoProc;\r
9688 \r
9689   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9690   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9691   saAttr.bInheritHandle = TRUE;\r
9692   saAttr.lpSecurityDescriptor = NULL;\r
9693 \r
9694   /*\r
9695    * The steps for redirecting child's STDOUT:\r
9696    *     1. Create anonymous pipe to be STDOUT for child.\r
9697    *     2. Create a noninheritable duplicate of read handle,\r
9698    *         and close the inheritable read handle.\r
9699    */\r
9700 \r
9701   /* Create a pipe for the child's STDOUT. */\r
9702   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9703     return GetLastError();\r
9704   }\r
9705 \r
9706   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9707   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9708                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9709                              FALSE,     /* not inherited */\r
9710                              DUPLICATE_SAME_ACCESS);\r
9711   if (! fSuccess) {\r
9712     return GetLastError();\r
9713   }\r
9714   CloseHandle(hChildStdoutRd);\r
9715 \r
9716   /*\r
9717    * The steps for redirecting child's STDIN:\r
9718    *     1. Create anonymous pipe to be STDIN for child.\r
9719    *     2. Create a noninheritable duplicate of write handle,\r
9720    *         and close the inheritable write handle.\r
9721    */\r
9722 \r
9723   /* Create a pipe for the child's STDIN. */\r
9724   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9725     return GetLastError();\r
9726   }\r
9727 \r
9728   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9729   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9730                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9731                              FALSE,     /* not inherited */\r
9732                              DUPLICATE_SAME_ACCESS);\r
9733   if (! fSuccess) {\r
9734     return GetLastError();\r
9735   }\r
9736   CloseHandle(hChildStdinWr);\r
9737 \r
9738   /* Arrange to (1) look in dir for the child .exe file, and\r
9739    * (2) have dir be the child's working directory.  Interpret\r
9740    * dir relative to the directory WinBoard loaded from. */\r
9741   GetCurrentDirectory(MSG_SIZ, buf);\r
9742   SetCurrentDirectory(installDir);\r
9743   SetCurrentDirectory(dir);\r
9744 \r
9745   /* Now create the child process. */\r
9746 \r
9747   siStartInfo.cb = sizeof(STARTUPINFO);\r
9748   siStartInfo.lpReserved = NULL;\r
9749   siStartInfo.lpDesktop = NULL;\r
9750   siStartInfo.lpTitle = NULL;\r
9751   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9752   siStartInfo.cbReserved2 = 0;\r
9753   siStartInfo.lpReserved2 = NULL;\r
9754   siStartInfo.hStdInput = hChildStdinRd;\r
9755   siStartInfo.hStdOutput = hChildStdoutWr;\r
9756   siStartInfo.hStdError = hChildStdoutWr;\r
9757 \r
9758   fSuccess = CreateProcess(NULL,\r
9759                            cmdLine,        /* command line */\r
9760                            NULL,           /* process security attributes */\r
9761                            NULL,           /* primary thread security attrs */\r
9762                            TRUE,           /* handles are inherited */\r
9763                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9764                            NULL,           /* use parent's environment */\r
9765                            NULL,\r
9766                            &siStartInfo, /* STARTUPINFO pointer */\r
9767                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9768 \r
9769   err = GetLastError();\r
9770   SetCurrentDirectory(buf); /* return to prev directory */\r
9771   if (! fSuccess) {\r
9772     return err;\r
9773   }\r
9774 \r
9775   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9776     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9777     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9778   }\r
9779 \r
9780   /* Close the handles we don't need in the parent */\r
9781   CloseHandle(piProcInfo.hThread);\r
9782   CloseHandle(hChildStdinRd);\r
9783   CloseHandle(hChildStdoutWr);\r
9784 \r
9785   /* Prepare return value */\r
9786   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9787   cp->kind = CPReal;\r
9788   cp->hProcess = piProcInfo.hProcess;\r
9789   cp->pid = piProcInfo.dwProcessId;\r
9790   cp->hFrom = hChildStdoutRdDup;\r
9791   cp->hTo = hChildStdinWrDup;\r
9792 \r
9793   *pr = (void *) cp;\r
9794 \r
9795   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9796      2000 where engines sometimes don't see the initial command(s)\r
9797      from WinBoard and hang.  I don't understand how that can happen,\r
9798      but the Sleep is harmless, so I've put it in.  Others have also\r
9799      reported what may be the same problem, so hopefully this will fix\r
9800      it for them too.  */\r
9801   Sleep(500);\r
9802 \r
9803   return NO_ERROR;\r
9804 }\r
9805 \r
9806 \r
9807 void\r
9808 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9809 {\r
9810   ChildProc *cp; int result;\r
9811 \r
9812   cp = (ChildProc *) pr;\r
9813   if (cp == NULL) return;\r
9814 \r
9815   switch (cp->kind) {\r
9816   case CPReal:\r
9817     /* TerminateProcess is considered harmful, so... */\r
9818     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9819     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9820     /* The following doesn't work because the chess program\r
9821        doesn't "have the same console" as WinBoard.  Maybe\r
9822        we could arrange for this even though neither WinBoard\r
9823        nor the chess program uses a console for stdio? */\r
9824     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9825 \r
9826     /* [AS] Special termination modes for misbehaving programs... */\r
9827     if( signal == 9 ) { \r
9828         result = TerminateProcess( cp->hProcess, 0 );\r
9829 \r
9830         if ( appData.debugMode) {\r
9831             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9832         }\r
9833     }\r
9834     else if( signal == 10 ) {\r
9835         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9836 \r
9837         if( dw != WAIT_OBJECT_0 ) {\r
9838             result = TerminateProcess( cp->hProcess, 0 );\r
9839 \r
9840             if ( appData.debugMode) {\r
9841                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9842             }\r
9843 \r
9844         }\r
9845     }\r
9846 \r
9847     CloseHandle(cp->hProcess);\r
9848     break;\r
9849 \r
9850   case CPComm:\r
9851     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9852     break;\r
9853 \r
9854   case CPSock:\r
9855     closesocket(cp->sock);\r
9856     WSACleanup();\r
9857     break;\r
9858 \r
9859   case CPRcmd:\r
9860     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9861     closesocket(cp->sock);\r
9862     closesocket(cp->sock2);\r
9863     WSACleanup();\r
9864     break;\r
9865   }\r
9866   free(cp);\r
9867 }\r
9868 \r
9869 void\r
9870 InterruptChildProcess(ProcRef pr)\r
9871 {\r
9872   ChildProc *cp;\r
9873 \r
9874   cp = (ChildProc *) pr;\r
9875   if (cp == NULL) return;\r
9876   switch (cp->kind) {\r
9877   case CPReal:\r
9878     /* The following doesn't work because the chess program\r
9879        doesn't "have the same console" as WinBoard.  Maybe\r
9880        we could arrange for this even though neither WinBoard\r
9881        nor the chess program uses a console for stdio */\r
9882     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9883     break;\r
9884 \r
9885   case CPComm:\r
9886   case CPSock:\r
9887     /* Can't interrupt */\r
9888     break;\r
9889 \r
9890   case CPRcmd:\r
9891     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9892     break;\r
9893   }\r
9894 }\r
9895 \r
9896 \r
9897 int\r
9898 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9899 {\r
9900   char cmdLine[MSG_SIZ];\r
9901 \r
9902   if (port[0] == NULLCHAR) {\r
9903     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9904   } else {\r
9905     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9906   }\r
9907   return StartChildProcess(cmdLine, "", pr);\r
9908 }\r
9909 \r
9910 \r
9911 /* Code to open TCP sockets */\r
9912 \r
9913 int\r
9914 OpenTCP(char *host, char *port, ProcRef *pr)\r
9915 {\r
9916   ChildProc *cp;\r
9917   int err;\r
9918   SOCKET s;\r
9919   struct sockaddr_in sa, mysa;\r
9920   struct hostent FAR *hp;\r
9921   unsigned short uport;\r
9922   WORD wVersionRequested;\r
9923   WSADATA wsaData;\r
9924 \r
9925   /* Initialize socket DLL */\r
9926   wVersionRequested = MAKEWORD(1, 1);\r
9927   err = WSAStartup(wVersionRequested, &wsaData);\r
9928   if (err != 0) return err;\r
9929 \r
9930   /* Make socket */\r
9931   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9932     err = WSAGetLastError();\r
9933     WSACleanup();\r
9934     return err;\r
9935   }\r
9936 \r
9937   /* Bind local address using (mostly) don't-care values.\r
9938    */\r
9939   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9940   mysa.sin_family = AF_INET;\r
9941   mysa.sin_addr.s_addr = INADDR_ANY;\r
9942   uport = (unsigned short) 0;\r
9943   mysa.sin_port = htons(uport);\r
9944   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9945       == SOCKET_ERROR) {\r
9946     err = WSAGetLastError();\r
9947     WSACleanup();\r
9948     return err;\r
9949   }\r
9950 \r
9951   /* Resolve remote host name */\r
9952   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9953   if (!(hp = gethostbyname(host))) {\r
9954     unsigned int b0, b1, b2, b3;\r
9955 \r
9956     err = WSAGetLastError();\r
9957 \r
9958     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9959       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9960       hp->h_addrtype = AF_INET;\r
9961       hp->h_length = 4;\r
9962       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9963       hp->h_addr_list[0] = (char *) malloc(4);\r
9964       hp->h_addr_list[0][0] = (char) b0;\r
9965       hp->h_addr_list[0][1] = (char) b1;\r
9966       hp->h_addr_list[0][2] = (char) b2;\r
9967       hp->h_addr_list[0][3] = (char) b3;\r
9968     } else {\r
9969       WSACleanup();\r
9970       return err;\r
9971     }\r
9972   }\r
9973   sa.sin_family = hp->h_addrtype;\r
9974   uport = (unsigned short) atoi(port);\r
9975   sa.sin_port = htons(uport);\r
9976   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9977 \r
9978   /* Make connection */\r
9979   if (connect(s, (struct sockaddr *) &sa,\r
9980               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9981     err = WSAGetLastError();\r
9982     WSACleanup();\r
9983     return err;\r
9984   }\r
9985 \r
9986   /* Prepare return value */\r
9987   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9988   cp->kind = CPSock;\r
9989   cp->sock = s;\r
9990   *pr = (ProcRef *) cp;\r
9991 \r
9992   return NO_ERROR;\r
9993 }\r
9994 \r
9995 int\r
9996 OpenCommPort(char *name, ProcRef *pr)\r
9997 {\r
9998   HANDLE h;\r
9999   COMMTIMEOUTS ct;\r
10000   ChildProc *cp;\r
10001   char fullname[MSG_SIZ];\r
10002 \r
10003   if (*name != '\\')\r
10004     sprintf(fullname, "\\\\.\\%s", name);\r
10005   else\r
10006     strcpy(fullname, name);\r
10007 \r
10008   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10009                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10010   if (h == (HANDLE) -1) {\r
10011     return GetLastError();\r
10012   }\r
10013   hCommPort = h;\r
10014 \r
10015   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10016 \r
10017   /* Accumulate characters until a 100ms pause, then parse */\r
10018   ct.ReadIntervalTimeout = 100;\r
10019   ct.ReadTotalTimeoutMultiplier = 0;\r
10020   ct.ReadTotalTimeoutConstant = 0;\r
10021   ct.WriteTotalTimeoutMultiplier = 0;\r
10022   ct.WriteTotalTimeoutConstant = 0;\r
10023   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10024 \r
10025   /* Prepare return value */\r
10026   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10027   cp->kind = CPComm;\r
10028   cp->hFrom = h;\r
10029   cp->hTo = h;\r
10030   *pr = (ProcRef *) cp;\r
10031 \r
10032   return NO_ERROR;\r
10033 }\r
10034 \r
10035 int\r
10036 OpenLoopback(ProcRef *pr)\r
10037 {\r
10038   DisplayFatalError("Not implemented", 0, 1);\r
10039   return NO_ERROR;\r
10040 }\r
10041 \r
10042 \r
10043 int\r
10044 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10045 {\r
10046   ChildProc *cp;\r
10047   int err;\r
10048   SOCKET s, s2, s3;\r
10049   struct sockaddr_in sa, mysa;\r
10050   struct hostent FAR *hp;\r
10051   unsigned short uport;\r
10052   WORD wVersionRequested;\r
10053   WSADATA wsaData;\r
10054   int fromPort;\r
10055   char stderrPortStr[MSG_SIZ];\r
10056 \r
10057   /* Initialize socket DLL */\r
10058   wVersionRequested = MAKEWORD(1, 1);\r
10059   err = WSAStartup(wVersionRequested, &wsaData);\r
10060   if (err != 0) return err;\r
10061 \r
10062   /* Resolve remote host name */\r
10063   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10064   if (!(hp = gethostbyname(host))) {\r
10065     unsigned int b0, b1, b2, b3;\r
10066 \r
10067     err = WSAGetLastError();\r
10068 \r
10069     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10070       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10071       hp->h_addrtype = AF_INET;\r
10072       hp->h_length = 4;\r
10073       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10074       hp->h_addr_list[0] = (char *) malloc(4);\r
10075       hp->h_addr_list[0][0] = (char) b0;\r
10076       hp->h_addr_list[0][1] = (char) b1;\r
10077       hp->h_addr_list[0][2] = (char) b2;\r
10078       hp->h_addr_list[0][3] = (char) b3;\r
10079     } else {\r
10080       WSACleanup();\r
10081       return err;\r
10082     }\r
10083   }\r
10084   sa.sin_family = hp->h_addrtype;\r
10085   uport = (unsigned short) 514;\r
10086   sa.sin_port = htons(uport);\r
10087   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10088 \r
10089   /* Bind local socket to unused "privileged" port address\r
10090    */\r
10091   s = INVALID_SOCKET;\r
10092   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10093   mysa.sin_family = AF_INET;\r
10094   mysa.sin_addr.s_addr = INADDR_ANY;\r
10095   for (fromPort = 1023;; fromPort--) {\r
10096     if (fromPort < 0) {\r
10097       WSACleanup();\r
10098       return WSAEADDRINUSE;\r
10099     }\r
10100     if (s == INVALID_SOCKET) {\r
10101       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10102         err = WSAGetLastError();\r
10103         WSACleanup();\r
10104         return err;\r
10105       }\r
10106     }\r
10107     uport = (unsigned short) fromPort;\r
10108     mysa.sin_port = htons(uport);\r
10109     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10110         == SOCKET_ERROR) {\r
10111       err = WSAGetLastError();\r
10112       if (err == WSAEADDRINUSE) continue;\r
10113       WSACleanup();\r
10114       return err;\r
10115     }\r
10116     if (connect(s, (struct sockaddr *) &sa,\r
10117       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10118       err = WSAGetLastError();\r
10119       if (err == WSAEADDRINUSE) {\r
10120         closesocket(s);\r
10121         s = -1;\r
10122         continue;\r
10123       }\r
10124       WSACleanup();\r
10125       return err;\r
10126     }\r
10127     break;\r
10128   }\r
10129 \r
10130   /* Bind stderr local socket to unused "privileged" port address\r
10131    */\r
10132   s2 = INVALID_SOCKET;\r
10133   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10134   mysa.sin_family = AF_INET;\r
10135   mysa.sin_addr.s_addr = INADDR_ANY;\r
10136   for (fromPort = 1023;; fromPort--) {\r
10137     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10138     if (fromPort < 0) {\r
10139       (void) closesocket(s);\r
10140       WSACleanup();\r
10141       return WSAEADDRINUSE;\r
10142     }\r
10143     if (s2 == INVALID_SOCKET) {\r
10144       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10145         err = WSAGetLastError();\r
10146         closesocket(s);\r
10147         WSACleanup();\r
10148         return err;\r
10149       }\r
10150     }\r
10151     uport = (unsigned short) fromPort;\r
10152     mysa.sin_port = htons(uport);\r
10153     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10154         == SOCKET_ERROR) {\r
10155       err = WSAGetLastError();\r
10156       if (err == WSAEADDRINUSE) continue;\r
10157       (void) closesocket(s);\r
10158       WSACleanup();\r
10159       return err;\r
10160     }\r
10161     if (listen(s2, 1) == SOCKET_ERROR) {\r
10162       err = WSAGetLastError();\r
10163       if (err == WSAEADDRINUSE) {\r
10164         closesocket(s2);\r
10165         s2 = INVALID_SOCKET;\r
10166         continue;\r
10167       }\r
10168       (void) closesocket(s);\r
10169       (void) closesocket(s2);\r
10170       WSACleanup();\r
10171       return err;\r
10172     }\r
10173     break;\r
10174   }\r
10175   prevStderrPort = fromPort; // remember port used\r
10176   sprintf(stderrPortStr, "%d", fromPort);\r
10177 \r
10178   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10179     err = WSAGetLastError();\r
10180     (void) closesocket(s);\r
10181     (void) closesocket(s2);\r
10182     WSACleanup();\r
10183     return err;\r
10184   }\r
10185 \r
10186   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10187     err = WSAGetLastError();\r
10188     (void) closesocket(s);\r
10189     (void) closesocket(s2);\r
10190     WSACleanup();\r
10191     return err;\r
10192   }\r
10193   if (*user == NULLCHAR) user = UserName();\r
10194   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10195     err = WSAGetLastError();\r
10196     (void) closesocket(s);\r
10197     (void) closesocket(s2);\r
10198     WSACleanup();\r
10199     return err;\r
10200   }\r
10201   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10202     err = WSAGetLastError();\r
10203     (void) closesocket(s);\r
10204     (void) closesocket(s2);\r
10205     WSACleanup();\r
10206     return err;\r
10207   }\r
10208 \r
10209   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10210     err = WSAGetLastError();\r
10211     (void) closesocket(s);\r
10212     (void) closesocket(s2);\r
10213     WSACleanup();\r
10214     return err;\r
10215   }\r
10216   (void) closesocket(s2);  /* Stop listening */\r
10217 \r
10218   /* Prepare return value */\r
10219   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10220   cp->kind = CPRcmd;\r
10221   cp->sock = s;\r
10222   cp->sock2 = s3;\r
10223   *pr = (ProcRef *) cp;\r
10224 \r
10225   return NO_ERROR;\r
10226 }\r
10227 \r
10228 \r
10229 InputSourceRef\r
10230 AddInputSource(ProcRef pr, int lineByLine,\r
10231                InputCallback func, VOIDSTAR closure)\r
10232 {\r
10233   InputSource *is, *is2 = NULL;\r
10234   ChildProc *cp = (ChildProc *) pr;\r
10235 \r
10236   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10237   is->lineByLine = lineByLine;\r
10238   is->func = func;\r
10239   is->closure = closure;\r
10240   is->second = NULL;\r
10241   is->next = is->buf;\r
10242   if (pr == NoProc) {\r
10243     is->kind = CPReal;\r
10244     consoleInputSource = is;\r
10245   } else {\r
10246     is->kind = cp->kind;\r
10247     /* \r
10248         [AS] Try to avoid a race condition if the thread is given control too early:\r
10249         we create all threads suspended so that the is->hThread variable can be\r
10250         safely assigned, then let the threads start with ResumeThread.\r
10251     */\r
10252     switch (cp->kind) {\r
10253     case CPReal:\r
10254       is->hFile = cp->hFrom;\r
10255       cp->hFrom = NULL; /* now owned by InputThread */\r
10256       is->hThread =\r
10257         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10258                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10259       break;\r
10260 \r
10261     case CPComm:\r
10262       is->hFile = cp->hFrom;\r
10263       cp->hFrom = NULL; /* now owned by InputThread */\r
10264       is->hThread =\r
10265         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10266                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10267       break;\r
10268 \r
10269     case CPSock:\r
10270       is->sock = cp->sock;\r
10271       is->hThread =\r
10272         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10273                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10274       break;\r
10275 \r
10276     case CPRcmd:\r
10277       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10278       *is2 = *is;\r
10279       is->sock = cp->sock;\r
10280       is->second = is2;\r
10281       is2->sock = cp->sock2;\r
10282       is2->second = is2;\r
10283       is->hThread =\r
10284         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10285                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10286       is2->hThread =\r
10287         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10288                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10289       break;\r
10290     }\r
10291 \r
10292     if( is->hThread != NULL ) {\r
10293         ResumeThread( is->hThread );\r
10294     }\r
10295 \r
10296     if( is2 != NULL && is2->hThread != NULL ) {\r
10297         ResumeThread( is2->hThread );\r
10298     }\r
10299   }\r
10300 \r
10301   return (InputSourceRef) is;\r
10302 }\r
10303 \r
10304 void\r
10305 RemoveInputSource(InputSourceRef isr)\r
10306 {\r
10307   InputSource *is;\r
10308 \r
10309   is = (InputSource *) isr;\r
10310   is->hThread = NULL;  /* tell thread to stop */\r
10311   CloseHandle(is->hThread);\r
10312   if (is->second != NULL) {\r
10313     is->second->hThread = NULL;\r
10314     CloseHandle(is->second->hThread);\r
10315   }\r
10316 }\r
10317 \r
10318 \r
10319 int\r
10320 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10321 {\r
10322   DWORD dOutCount;\r
10323   int outCount = SOCKET_ERROR;\r
10324   ChildProc *cp = (ChildProc *) pr;\r
10325   static OVERLAPPED ovl;\r
10326 \r
10327   if (pr == NoProc) {\r
10328     ConsoleOutput(message, count, FALSE);\r
10329     return count;\r
10330   } \r
10331 \r
10332   if (ovl.hEvent == NULL) {\r
10333     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10334   }\r
10335   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10336 \r
10337   switch (cp->kind) {\r
10338   case CPSock:\r
10339   case CPRcmd:\r
10340     outCount = send(cp->sock, message, count, 0);\r
10341     if (outCount == SOCKET_ERROR) {\r
10342       *outError = WSAGetLastError();\r
10343     } else {\r
10344       *outError = NO_ERROR;\r
10345     }\r
10346     break;\r
10347 \r
10348   case CPReal:\r
10349     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10350                   &dOutCount, NULL)) {\r
10351       *outError = NO_ERROR;\r
10352       outCount = (int) dOutCount;\r
10353     } else {\r
10354       *outError = GetLastError();\r
10355     }\r
10356     break;\r
10357 \r
10358   case CPComm:\r
10359     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10360                             &dOutCount, &ovl);\r
10361     if (*outError == NO_ERROR) {\r
10362       outCount = (int) dOutCount;\r
10363     }\r
10364     break;\r
10365   }\r
10366   return outCount;\r
10367 }\r
10368 \r
10369 int\r
10370 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10371                        long msdelay)\r
10372 {\r
10373   /* Ignore delay, not implemented for WinBoard */\r
10374   return OutputToProcess(pr, message, count, outError);\r
10375 }\r
10376 \r
10377 \r
10378 void\r
10379 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10380                         char *buf, int count, int error)\r
10381 {\r
10382   DisplayFatalError("Not implemented", 0, 1);\r
10383 }\r
10384 \r
10385 /* see wgamelist.c for Game List functions */\r
10386 /* see wedittags.c for Edit Tags functions */\r
10387 \r
10388 \r
10389 VOID\r
10390 ICSInitScript()\r
10391 {\r
10392   FILE *f;\r
10393   char buf[MSG_SIZ];\r
10394   char *dummy;\r
10395 \r
10396   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10397     f = fopen(buf, "r");\r
10398     if (f != NULL) {\r
10399       ProcessICSInitScript(f);\r
10400       fclose(f);\r
10401     }\r
10402   }\r
10403 }\r
10404 \r
10405 \r
10406 VOID\r
10407 StartAnalysisClock()\r
10408 {\r
10409   if (analysisTimerEvent) return;\r
10410   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10411                                         (UINT) 2000, NULL);\r
10412 }\r
10413 \r
10414 LRESULT CALLBACK\r
10415 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10416 {\r
10417   static HANDLE hwndText;\r
10418   RECT rect;\r
10419   static int sizeX, sizeY;\r
10420   int newSizeX, newSizeY, flags;\r
10421   MINMAXINFO *mmi;\r
10422 \r
10423   switch (message) {\r
10424   case WM_INITDIALOG: /* message: initialize dialog box */\r
10425     /* Initialize the dialog items */\r
10426     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10427     SetWindowText(hDlg, analysisTitle);\r
10428     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10429     /* Size and position the dialog */\r
10430     if (!analysisDialog) {\r
10431       analysisDialog = hDlg;\r
10432       flags = SWP_NOZORDER;\r
10433       GetClientRect(hDlg, &rect);\r
10434       sizeX = rect.right;\r
10435       sizeY = rect.bottom;\r
10436       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10437           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10438         WINDOWPLACEMENT wp;\r
10439         EnsureOnScreen(&analysisX, &analysisY);\r
10440         wp.length = sizeof(WINDOWPLACEMENT);\r
10441         wp.flags = 0;\r
10442         wp.showCmd = SW_SHOW;\r
10443         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10444         wp.rcNormalPosition.left = analysisX;\r
10445         wp.rcNormalPosition.right = analysisX + analysisW;\r
10446         wp.rcNormalPosition.top = analysisY;\r
10447         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10448         SetWindowPlacement(hDlg, &wp);\r
10449 \r
10450         GetClientRect(hDlg, &rect);\r
10451         newSizeX = rect.right;\r
10452         newSizeY = rect.bottom;\r
10453         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10454                               newSizeX, newSizeY);\r
10455         sizeX = newSizeX;\r
10456         sizeY = newSizeY;\r
10457       }\r
10458     }\r
10459     return FALSE;\r
10460 \r
10461   case WM_COMMAND: /* message: received a command */\r
10462     switch (LOWORD(wParam)) {\r
10463     case IDCANCEL:\r
10464       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10465           ExitAnalyzeMode();\r
10466           ModeHighlight();\r
10467           return TRUE;\r
10468       }\r
10469       EditGameEvent();\r
10470       return TRUE;\r
10471     default:\r
10472       break;\r
10473     }\r
10474     break;\r
10475 \r
10476   case WM_SIZE:\r
10477     newSizeX = LOWORD(lParam);\r
10478     newSizeY = HIWORD(lParam);\r
10479     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10480     sizeX = newSizeX;\r
10481     sizeY = newSizeY;\r
10482     break;\r
10483 \r
10484   case WM_GETMINMAXINFO:\r
10485     /* Prevent resizing window too small */\r
10486     mmi = (MINMAXINFO *) lParam;\r
10487     mmi->ptMinTrackSize.x = 100;\r
10488     mmi->ptMinTrackSize.y = 100;\r
10489     break;\r
10490   }\r
10491   return FALSE;\r
10492 }\r
10493 \r
10494 VOID\r
10495 AnalysisPopUp(char* title, char* str)\r
10496 {\r
10497   FARPROC lpProc;\r
10498   char *p, *q;\r
10499 \r
10500   /* [AS] */\r
10501   EngineOutputPopUp();\r
10502   return;\r
10503 \r
10504   if (str == NULL) str = "";\r
10505   p = (char *) malloc(2 * strlen(str) + 2);\r
10506   q = p;\r
10507   while (*str) {\r
10508     if (*str == '\n') *q++ = '\r';\r
10509     *q++ = *str++;\r
10510   }\r
10511   *q = NULLCHAR;\r
10512   if (analysisText != NULL) free(analysisText);\r
10513   analysisText = p;\r
10514 \r
10515   if (analysisDialog) {\r
10516     SetWindowText(analysisDialog, title);\r
10517     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10518     ShowWindow(analysisDialog, SW_SHOW);\r
10519   } else {\r
10520     analysisTitle = title;\r
10521     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10522     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10523                  hwndMain, (DLGPROC)lpProc);\r
10524     FreeProcInstance(lpProc);\r
10525   }\r
10526   analysisDialogUp = TRUE;  \r
10527 }\r
10528 \r
10529 VOID\r
10530 AnalysisPopDown()\r
10531 {\r
10532   if (analysisDialog) {\r
10533     ShowWindow(analysisDialog, SW_HIDE);\r
10534   }\r
10535   analysisDialogUp = FALSE;  \r
10536 }\r
10537 \r
10538 \r
10539 VOID\r
10540 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10541 {\r
10542   highlightInfo.sq[0].x = fromX;\r
10543   highlightInfo.sq[0].y = fromY;\r
10544   highlightInfo.sq[1].x = toX;\r
10545   highlightInfo.sq[1].y = toY;\r
10546 }\r
10547 \r
10548 VOID\r
10549 ClearHighlights()\r
10550 {\r
10551   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10552     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10553 }\r
10554 \r
10555 VOID\r
10556 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10557 {\r
10558   premoveHighlightInfo.sq[0].x = fromX;\r
10559   premoveHighlightInfo.sq[0].y = fromY;\r
10560   premoveHighlightInfo.sq[1].x = toX;\r
10561   premoveHighlightInfo.sq[1].y = toY;\r
10562 }\r
10563 \r
10564 VOID\r
10565 ClearPremoveHighlights()\r
10566 {\r
10567   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10568     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10569 }\r
10570 \r
10571 VOID\r
10572 ShutDownFrontEnd()\r
10573 {\r
10574   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10575   DeleteClipboardTempFiles();\r
10576 }\r
10577 \r
10578 void\r
10579 BoardToTop()\r
10580 {\r
10581     if (IsIconic(hwndMain))\r
10582       ShowWindow(hwndMain, SW_RESTORE);\r
10583 \r
10584     SetActiveWindow(hwndMain);\r
10585 }\r
10586 \r
10587 /*\r
10588  * Prototypes for animation support routines\r
10589  */\r
10590 static void ScreenSquare(int column, int row, POINT * pt);\r
10591 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10592      POINT frames[], int * nFrames);\r
10593 \r
10594 \r
10595 void\r
10596 AnimateAtomicCapture(int toX, int toY, int nFrames)\r
10597 {       // [HGM] atomic: animate blast wave\r
10598         int i;\r
10599 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10600         explodeInfo.x = toX;\r
10601         explodeInfo.y = toY;\r
10602         for(i=0; i<nFrames; i++) {\r
10603             explodeInfo.radius = (i*180)/(nFrames-1);\r
10604             DrawPosition(FALSE, NULL);\r
10605             Sleep(appData.animSpeed);\r
10606         }\r
10607         explodeInfo.radius = 0;\r
10608         DrawPosition(TRUE, NULL);\r
10609 }\r
10610 \r
10611 #define kFactor 4\r
10612 \r
10613 void\r
10614 AnimateMove(board, fromX, fromY, toX, toY)\r
10615      Board board;\r
10616      int fromX;\r
10617      int fromY;\r
10618      int toX;\r
10619      int toY;\r
10620 {\r
10621   ChessSquare piece;\r
10622   POINT start, finish, mid;\r
10623   POINT frames[kFactor * 2 + 1];\r
10624   int nFrames, n;\r
10625 \r
10626   if (!appData.animate) return;\r
10627   if (doingSizing) return;\r
10628   if (fromY < 0 || fromX < 0) return;\r
10629   piece = board[fromY][fromX];\r
10630   if (piece >= EmptySquare) return;\r
10631 \r
10632   ScreenSquare(fromX, fromY, &start);\r
10633   ScreenSquare(toX, toY, &finish);\r
10634 \r
10635   /* All pieces except knights move in straight line */\r
10636   if (piece != WhiteKnight && piece != BlackKnight) {\r
10637     mid.x = start.x + (finish.x - start.x) / 2;\r
10638     mid.y = start.y + (finish.y - start.y) / 2;\r
10639   } else {\r
10640     /* Knight: make diagonal movement then straight */\r
10641     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10642        mid.x = start.x + (finish.x - start.x) / 2;\r
10643        mid.y = finish.y;\r
10644      } else {\r
10645        mid.x = finish.x;\r
10646        mid.y = start.y + (finish.y - start.y) / 2;\r
10647      }\r
10648   }\r
10649   \r
10650   /* Don't use as many frames for very short moves */\r
10651   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10652     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10653   else\r
10654     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10655 \r
10656   animInfo.from.x = fromX;\r
10657   animInfo.from.y = fromY;\r
10658   animInfo.to.x = toX;\r
10659   animInfo.to.y = toY;\r
10660   animInfo.lastpos = start;\r
10661   animInfo.piece = piece;\r
10662   for (n = 0; n < nFrames; n++) {\r
10663     animInfo.pos = frames[n];\r
10664     DrawPosition(FALSE, NULL);\r
10665     animInfo.lastpos = animInfo.pos;\r
10666     Sleep(appData.animSpeed);\r
10667   }\r
10668   animInfo.pos = finish;\r
10669   DrawPosition(FALSE, NULL);\r
10670   animInfo.piece = EmptySquare;\r
10671   if(gameInfo.variant == VariantAtomic && board[toY][toX] != EmptySquare)\r
10672     AnimateAtomicCapture(toX, toY, 2*nFrames);\r
10673 }\r
10674 \r
10675 /*      Convert board position to corner of screen rect and color       */\r
10676 \r
10677 static void\r
10678 ScreenSquare(column, row, pt)\r
10679      int column; int row; POINT * pt;\r
10680 {\r
10681   if (flipView) {\r
10682     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10683     pt->y = lineGap + row * (squareSize + lineGap);\r
10684   } else {\r
10685     pt->x = lineGap + column * (squareSize + lineGap);\r
10686     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10687   }\r
10688 }\r
10689 \r
10690 /*      Generate a series of frame coords from start->mid->finish.\r
10691         The movement rate doubles until the half way point is\r
10692         reached, then halves back down to the final destination,\r
10693         which gives a nice slow in/out effect. The algorithmn\r
10694         may seem to generate too many intermediates for short\r
10695         moves, but remember that the purpose is to attract the\r
10696         viewers attention to the piece about to be moved and\r
10697         then to where it ends up. Too few frames would be less\r
10698         noticeable.                                             */\r
10699 \r
10700 static void\r
10701 Tween(start, mid, finish, factor, frames, nFrames)\r
10702      POINT * start; POINT * mid;\r
10703      POINT * finish; int factor;\r
10704      POINT frames[]; int * nFrames;\r
10705 {\r
10706   int n, fraction = 1, count = 0;\r
10707 \r
10708   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10709   for (n = 0; n < factor; n++)\r
10710     fraction *= 2;\r
10711   for (n = 0; n < factor; n++) {\r
10712     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10713     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10714     count ++;\r
10715     fraction = fraction / 2;\r
10716   }\r
10717   \r
10718   /* Midpoint */\r
10719   frames[count] = *mid;\r
10720   count ++;\r
10721   \r
10722   /* Slow out, stepping 1/2, then 1/4, ... */\r
10723   fraction = 2;\r
10724   for (n = 0; n < factor; n++) {\r
10725     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10726     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10727     count ++;\r
10728     fraction = fraction * 2;\r
10729   }\r
10730   *nFrames = count;\r
10731 }\r
10732 \r
10733 void\r
10734 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10735 {\r
10736 #if 0\r
10737     char buf[256];\r
10738 \r
10739     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10740         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10741 \r
10742     OutputDebugString( buf );\r
10743 #endif\r
10744 \r
10745     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10746 \r
10747     EvalGraphSet( first, last, current, pvInfoList );\r
10748 }\r
10749 \r
10750 void SetProgramStats( FrontEndProgramStats * stats )\r
10751 {\r
10752 #if 0\r
10753     char buf[1024];\r
10754 \r
10755     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10756         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10757 \r
10758     OutputDebugString( buf );\r
10759 #endif\r
10760 \r
10761     EngineOutputUpdate( stats );\r
10762 }\r