modified clock layout
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int fromX, fromY, toX, toY, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo;\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, 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   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1248   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1249 \r
1250 #ifdef ZIPPY\r
1251   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1254   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1255   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1258   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1259   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1260   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1261   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1262   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1263   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1264     FALSE },\r
1265   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1266   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1269   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1270   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1271   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1272     FALSE },\r
1273   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1275   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1276   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1277   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1278   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1281   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1282   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1285   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1286   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1287   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1288   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1289   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1290   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1291   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1292 #endif\r
1293   /* [HGM] options for broadcasting and time odds */\r
1294   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1295   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1296   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1297   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1298   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1299   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1300   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1301   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1302   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1303   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1304   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1305   { NULL, ArgNone, NULL, FALSE }\r
1306 };\r
1307 \r
1308 \r
1309 /* Kludge for indirection files on command line */\r
1310 char* lastIndirectionFilename;\r
1311 ArgDescriptor argDescriptorIndirection =\r
1312 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1313 \r
1314 \r
1315 VOID\r
1316 ExitArgError(char *msg, char *badArg)\r
1317 {\r
1318   char buf[MSG_SIZ];\r
1319 \r
1320   sprintf(buf, "%s %s", msg, badArg);\r
1321   DisplayFatalError(buf, 0, 2);\r
1322   exit(2);\r
1323 }\r
1324 \r
1325 /* Command line font name parser.  NULL name means do nothing.\r
1326    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1327    For backward compatibility, syntax without the colon is also\r
1328    accepted, but font names with digits in them won't work in that case.\r
1329 */\r
1330 VOID\r
1331 ParseFontName(char *name, MyFontParams *mfp)\r
1332 {\r
1333   char *p, *q;\r
1334   if (name == NULL) return;\r
1335   p = name;\r
1336   q = strchr(p, ':');\r
1337   if (q) {\r
1338     if (q - p >= sizeof(mfp->faceName))\r
1339       ExitArgError("Font name too long:", name);\r
1340     memcpy(mfp->faceName, p, q - p);\r
1341     mfp->faceName[q - p] = NULLCHAR;\r
1342     p = q + 1;\r
1343   } else {\r
1344     q = mfp->faceName;\r
1345     while (*p && !isdigit(*p)) {\r
1346       *q++ = *p++;\r
1347       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1348         ExitArgError("Font name too long:", name);\r
1349     }\r
1350     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1351     *q = NULLCHAR;\r
1352   }\r
1353   if (!*p) ExitArgError("Font point size missing:", name);\r
1354   mfp->pointSize = (float) atof(p);\r
1355   mfp->bold = (strchr(p, 'b') != NULL);\r
1356   mfp->italic = (strchr(p, 'i') != NULL);\r
1357   mfp->underline = (strchr(p, 'u') != NULL);\r
1358   mfp->strikeout = (strchr(p, 's') != NULL);\r
1359 }\r
1360 \r
1361 /* Color name parser.\r
1362    X version accepts X color names, but this one\r
1363    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1364 COLORREF\r
1365 ParseColorName(char *name)\r
1366 {\r
1367   int red, green, blue, count;\r
1368   char buf[MSG_SIZ];\r
1369 \r
1370   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1371   if (count != 3) {\r
1372     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1373       &red, &green, &blue);\r
1374   }\r
1375   if (count != 3) {\r
1376     sprintf(buf, "Can't parse color name %s", name);\r
1377     DisplayError(buf, 0);\r
1378     return RGB(0, 0, 0);\r
1379   }\r
1380   return PALETTERGB(red, green, blue);\r
1381 }\r
1382 \r
1383 \r
1384 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1385 {\r
1386   char *e = argValue;\r
1387   int eff = 0;\r
1388 \r
1389   while (*e) {\r
1390     if (*e == 'b')      eff |= CFE_BOLD;\r
1391     else if (*e == 'i') eff |= CFE_ITALIC;\r
1392     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1393     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1394     else if (*e == '#' || isdigit(*e)) break;\r
1395     e++;\r
1396   }\r
1397   *effects = eff;\r
1398   *color   = ParseColorName(e);\r
1399 }\r
1400 \r
1401 \r
1402 BoardSize\r
1403 ParseBoardSize(char *name)\r
1404 {\r
1405   BoardSize bs = SizeTiny;\r
1406   while (sizeInfo[bs].name != NULL) {\r
1407     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1408     bs++;\r
1409   }\r
1410   ExitArgError("Unrecognized board size value", name);\r
1411   return bs; /* not reached */\r
1412 }\r
1413 \r
1414 \r
1415 char\r
1416 StringGet(void *getClosure)\r
1417 {\r
1418   char **p = (char **) getClosure;\r
1419   return *((*p)++);\r
1420 }\r
1421 \r
1422 char\r
1423 FileGet(void *getClosure)\r
1424 {\r
1425   int c;\r
1426   FILE* f = (FILE*) getClosure;\r
1427 \r
1428   c = getc(f);\r
1429   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1430   if (c == EOF)\r
1431     return NULLCHAR;\r
1432   else\r
1433     return (char) c;\r
1434 }\r
1435 \r
1436 /* Parse settings file named "name". If file found, return the\r
1437    full name in fullname and return TRUE; else return FALSE */\r
1438 BOOLEAN\r
1439 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1440 {\r
1441   char *dummy;\r
1442   FILE *f;\r
1443   int ok; char buf[MSG_SIZ];\r
1444 \r
1445   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1446   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1447     sprintf(buf, "%s.ini", name);\r
1448     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1449   }\r
1450   if (ok) {\r
1451     f = fopen(fullname, "r");\r
1452     if (f != NULL) {\r
1453       ParseArgs(FileGet, f);\r
1454       fclose(f);\r
1455       return TRUE;\r
1456     }\r
1457   }\r
1458   return FALSE;\r
1459 }\r
1460 \r
1461 VOID\r
1462 ParseArgs(GetFunc get, void *cl)\r
1463 {\r
1464   char argName[ARG_MAX];\r
1465   char argValue[ARG_MAX];\r
1466   ArgDescriptor *ad;\r
1467   char start;\r
1468   char *q;\r
1469   int i, octval;\r
1470   char ch;\r
1471   int posarg = 0;\r
1472 \r
1473   ch = get(cl);\r
1474   for (;;) {\r
1475     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1476     if (ch == NULLCHAR) break;\r
1477     if (ch == ';') {\r
1478       /* Comment to end of line */\r
1479       ch = get(cl);\r
1480       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1481       continue;\r
1482     } else if (ch == '/' || ch == '-') {\r
1483       /* Switch */\r
1484       q = argName;\r
1485       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1486              ch != '\n' && ch != '\t') {\r
1487         *q++ = ch;\r
1488         ch = get(cl);\r
1489       }\r
1490       *q = NULLCHAR;\r
1491 \r
1492       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1493         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1494 \r
1495       if (ad->argName == NULL)\r
1496         ExitArgError("Unrecognized argument", argName);\r
1497 \r
1498     } else if (ch == '@') {\r
1499       /* Indirection file */\r
1500       ad = &argDescriptorIndirection;\r
1501       ch = get(cl);\r
1502     } else {\r
1503       /* Positional argument */\r
1504       ad = &argDescriptors[posarg++];\r
1505       strcpy(argName, ad->argName);\r
1506     }\r
1507 \r
1508     if (ad->argType == ArgTrue) {\r
1509       *(Boolean *) ad->argLoc = TRUE;\r
1510       continue;\r
1511     }\r
1512     if (ad->argType == ArgFalse) {\r
1513       *(Boolean *) ad->argLoc = FALSE;\r
1514       continue;\r
1515     }\r
1516 \r
1517     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1518     if (ch == NULLCHAR || ch == '\n') {\r
1519       ExitArgError("No value provided for argument", argName);\r
1520     }\r
1521     q = argValue;\r
1522     if (ch == '{') {\r
1523       // Quoting with { }.  No characters have to (or can) be escaped.\r
1524       // Thus the string cannot contain a '}' character.\r
1525       start = ch;\r
1526       ch = get(cl);\r
1527       while (start) {\r
1528         switch (ch) {\r
1529         case NULLCHAR:\r
1530           start = NULLCHAR;\r
1531           break;\r
1532           \r
1533         case '}':\r
1534           ch = get(cl);\r
1535           start = NULLCHAR;\r
1536           break;\r
1537 \r
1538         default:\r
1539           *q++ = ch;\r
1540           ch = get(cl);\r
1541           break;\r
1542         }\r
1543       }   \r
1544     } else if (ch == '\'' || ch == '"') {\r
1545       // Quoting with ' ' or " ", with \ as escape character.\r
1546       // Inconvenient for long strings that may contain Windows filenames.\r
1547       start = ch;\r
1548       ch = get(cl);\r
1549       while (start) {\r
1550         switch (ch) {\r
1551         case NULLCHAR:\r
1552           start = NULLCHAR;\r
1553           break;\r
1554 \r
1555         default:\r
1556         not_special:\r
1557           *q++ = ch;\r
1558           ch = get(cl);\r
1559           break;\r
1560 \r
1561         case '\'':\r
1562         case '\"':\r
1563           if (ch == start) {\r
1564             ch = get(cl);\r
1565             start = NULLCHAR;\r
1566             break;\r
1567           } else {\r
1568             goto not_special;\r
1569           }\r
1570 \r
1571         case '\\':\r
1572           if (ad->argType == ArgFilename\r
1573               || ad->argType == ArgSettingsFilename) {\r
1574               goto not_special;\r
1575           }\r
1576           ch = get(cl);\r
1577           switch (ch) {\r
1578           case NULLCHAR:\r
1579             ExitArgError("Incomplete \\ escape in value for", argName);\r
1580             break;\r
1581           case 'n':\r
1582             *q++ = '\n';\r
1583             ch = get(cl);\r
1584             break;\r
1585           case 'r':\r
1586             *q++ = '\r';\r
1587             ch = get(cl);\r
1588             break;\r
1589           case 't':\r
1590             *q++ = '\t';\r
1591             ch = get(cl);\r
1592             break;\r
1593           case 'b':\r
1594             *q++ = '\b';\r
1595             ch = get(cl);\r
1596             break;\r
1597           case 'f':\r
1598             *q++ = '\f';\r
1599             ch = get(cl);\r
1600             break;\r
1601           default:\r
1602             octval = 0;\r
1603             for (i = 0; i < 3; i++) {\r
1604               if (ch >= '0' && ch <= '7') {\r
1605                 octval = octval*8 + (ch - '0');\r
1606                 ch = get(cl);\r
1607               } else {\r
1608                 break;\r
1609               }\r
1610             }\r
1611             if (i > 0) {\r
1612               *q++ = (char) octval;\r
1613             } else {\r
1614               *q++ = ch;\r
1615               ch = get(cl);\r
1616             }\r
1617             break;\r
1618           }\r
1619           break;\r
1620         }\r
1621       }\r
1622     } else {\r
1623       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1624         *q++ = ch;\r
1625         ch = get(cl);\r
1626       }\r
1627     }\r
1628     *q = NULLCHAR;\r
1629 \r
1630     switch (ad->argType) {\r
1631     case ArgInt:\r
1632       *(int *) ad->argLoc = atoi(argValue);\r
1633       break;\r
1634 \r
1635     case ArgFloat:\r
1636       *(float *) ad->argLoc = (float) atof(argValue);\r
1637       break;\r
1638 \r
1639     case ArgString:\r
1640     case ArgFilename:\r
1641       *(char **) ad->argLoc = strdup(argValue);\r
1642       break;\r
1643 \r
1644     case ArgSettingsFilename:\r
1645       {\r
1646         char fullname[MSG_SIZ];\r
1647         if (ParseSettingsFile(argValue, fullname)) {\r
1648           if (ad->argLoc != NULL) {\r
1649             *(char **) ad->argLoc = strdup(fullname);\r
1650           }\r
1651         } else {\r
1652           if (ad->argLoc != NULL) {\r
1653           } else {\r
1654             ExitArgError("Failed to open indirection file", argValue);\r
1655           }\r
1656         }\r
1657       }\r
1658       break;\r
1659 \r
1660     case ArgBoolean:\r
1661       switch (argValue[0]) {\r
1662       case 't':\r
1663       case 'T':\r
1664         *(Boolean *) ad->argLoc = TRUE;\r
1665         break;\r
1666       case 'f':\r
1667       case 'F':\r
1668         *(Boolean *) ad->argLoc = FALSE;\r
1669         break;\r
1670       default:\r
1671         ExitArgError("Unrecognized boolean argument value", argValue);\r
1672         break;\r
1673       }\r
1674       break;\r
1675 \r
1676     case ArgColor:\r
1677       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1678       break;\r
1679 \r
1680     case ArgAttribs: {\r
1681       ColorClass cc = (ColorClass)ad->argLoc;\r
1682       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1683       }\r
1684       break;\r
1685       \r
1686     case ArgBoardSize:\r
1687       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1688       break;\r
1689 \r
1690     case ArgFont:\r
1691       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1692       break;\r
1693 \r
1694     case ArgCommSettings:\r
1695       ParseCommSettings(argValue, &dcb);\r
1696       break;\r
1697 \r
1698     case ArgNone:\r
1699       ExitArgError("Unrecognized argument", argValue);\r
1700       break;\r
1701     case ArgTrue:\r
1702     case ArgFalse: ;\r
1703     }\r
1704   }\r
1705 }\r
1706 \r
1707 VOID\r
1708 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1709 {\r
1710   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1711   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1712   DeleteDC(hdc);\r
1713   lf->lfWidth = 0;\r
1714   lf->lfEscapement = 0;\r
1715   lf->lfOrientation = 0;\r
1716   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1717   lf->lfItalic = mfp->italic;\r
1718   lf->lfUnderline = mfp->underline;\r
1719   lf->lfStrikeOut = mfp->strikeout;\r
1720   lf->lfCharSet = DEFAULT_CHARSET;\r
1721   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1722   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1723   lf->lfQuality = DEFAULT_QUALITY;\r
1724   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1725   strcpy(lf->lfFaceName, mfp->faceName);\r
1726 }\r
1727 \r
1728 VOID\r
1729 CreateFontInMF(MyFont *mf)\r
1730 {\r
1731   LFfromMFP(&mf->lf, &mf->mfp);\r
1732   if (mf->hf) DeleteObject(mf->hf);\r
1733   mf->hf = CreateFontIndirect(&mf->lf);\r
1734 }\r
1735 \r
1736 VOID\r
1737 SetDefaultTextAttribs()\r
1738 {\r
1739   ColorClass cc;\r
1740   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1741     ParseAttribs(&textAttribs[cc].color, \r
1742                  &textAttribs[cc].effects, \r
1743                  defaultTextAttribs[cc]);\r
1744   }\r
1745 }\r
1746 \r
1747 VOID\r
1748 SetDefaultSounds()\r
1749 {\r
1750   ColorClass cc;\r
1751   SoundClass sc;\r
1752   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1753     textAttribs[cc].sound.name = strdup("");\r
1754     textAttribs[cc].sound.data = NULL;\r
1755   }\r
1756   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1757     sounds[sc].name = strdup("");\r
1758     sounds[sc].data = NULL;\r
1759   }\r
1760   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1761 }\r
1762 \r
1763 VOID\r
1764 LoadAllSounds()\r
1765 {\r
1766   ColorClass cc;\r
1767   SoundClass sc;\r
1768   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1769     MyLoadSound(&textAttribs[cc].sound);\r
1770   }\r
1771   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1772     MyLoadSound(&sounds[sc]);\r
1773   }\r
1774 }\r
1775 \r
1776 VOID\r
1777 InitAppData(LPSTR lpCmdLine)\r
1778 {\r
1779   int i, j;\r
1780   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1781   char *dummy, *p;\r
1782 \r
1783   programName = szAppName;\r
1784 \r
1785   /* Initialize to defaults */\r
1786   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1787   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1788   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1789   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1790   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1791   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1792   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1793   SetDefaultTextAttribs();\r
1794   SetDefaultSounds();\r
1795   appData.movesPerSession = MOVES_PER_SESSION;\r
1796   appData.initString = INIT_STRING;\r
1797   appData.secondInitString = INIT_STRING;\r
1798   appData.firstComputerString = COMPUTER_STRING;\r
1799   appData.secondComputerString = COMPUTER_STRING;\r
1800   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1801   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1802   appData.firstPlaysBlack = FALSE;\r
1803   appData.noChessProgram = FALSE;\r
1804   chessProgram = FALSE;\r
1805   appData.firstHost = FIRST_HOST;\r
1806   appData.secondHost = SECOND_HOST;\r
1807   appData.firstDirectory = FIRST_DIRECTORY;\r
1808   appData.secondDirectory = SECOND_DIRECTORY;\r
1809   appData.bitmapDirectory = "";\r
1810   appData.remoteShell = REMOTE_SHELL;\r
1811   appData.remoteUser = "";\r
1812   appData.timeDelay = TIME_DELAY;\r
1813   appData.timeControl = TIME_CONTROL;\r
1814   appData.timeIncrement = TIME_INCREMENT;\r
1815   appData.icsActive = FALSE;\r
1816   appData.icsHost = "";\r
1817   appData.icsPort = ICS_PORT;\r
1818   appData.icsCommPort = ICS_COMM_PORT;\r
1819   appData.icsLogon = ICS_LOGON;\r
1820   appData.icsHelper = "";\r
1821   appData.useTelnet = FALSE;\r
1822   appData.telnetProgram = TELNET_PROGRAM;\r
1823   appData.gateway = "";\r
1824   appData.loadGameFile = "";\r
1825   appData.loadGameIndex = 0;\r
1826   appData.saveGameFile = "";\r
1827   appData.autoSaveGames = FALSE;\r
1828   appData.loadPositionFile = "";\r
1829   appData.loadPositionIndex = 1;\r
1830   appData.savePositionFile = "";\r
1831   appData.matchMode = FALSE;\r
1832   appData.matchGames = 0;\r
1833   appData.monoMode = FALSE;\r
1834   appData.debugMode = FALSE;\r
1835   appData.clockMode = TRUE;\r
1836   boardSize = (BoardSize) -1; /* determine by screen size */\r
1837   appData.Iconic = FALSE; /*unused*/\r
1838   appData.searchTime = "";\r
1839   appData.searchDepth = 0;\r
1840   appData.showCoords = FALSE;\r
1841   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1842   appData.autoCallFlag = FALSE;\r
1843   appData.flipView = FALSE;\r
1844   appData.autoFlipView = TRUE;\r
1845   appData.cmailGameName = "";\r
1846   appData.alwaysPromoteToQueen = FALSE;\r
1847   appData.oldSaveStyle = FALSE;\r
1848   appData.quietPlay = FALSE;\r
1849   appData.showThinking = FALSE;\r
1850   appData.ponderNextMove = TRUE;\r
1851   appData.periodicUpdates = TRUE;\r
1852   appData.popupExitMessage = TRUE;\r
1853   appData.popupMoveErrors = FALSE;\r
1854   appData.autoObserve = FALSE;\r
1855   appData.autoComment = FALSE;\r
1856   appData.animate = TRUE;\r
1857   appData.animSpeed = 10;\r
1858   appData.animateDragging = TRUE;\r
1859   appData.highlightLastMove = TRUE;\r
1860   appData.getMoveList = TRUE;\r
1861   appData.testLegality = TRUE;\r
1862   appData.premove = TRUE;\r
1863   appData.premoveWhite = FALSE;\r
1864   appData.premoveWhiteText = "";\r
1865   appData.premoveBlack = FALSE;\r
1866   appData.premoveBlackText = "";\r
1867   appData.icsAlarm = TRUE;\r
1868   appData.icsAlarmTime = 5000;\r
1869   appData.autoRaiseBoard = TRUE;\r
1870   appData.localLineEditing = TRUE;\r
1871   appData.colorize = TRUE;\r
1872   appData.reuseFirst = TRUE;\r
1873   appData.reuseSecond = TRUE;\r
1874   appData.blindfold = FALSE;\r
1875   appData.icsEngineAnalyze = FALSE;\r
1876   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1877   dcb.DCBlength = sizeof(DCB);\r
1878   dcb.BaudRate = 9600;\r
1879   dcb.fBinary = TRUE;\r
1880   dcb.fParity = FALSE;\r
1881   dcb.fOutxCtsFlow = FALSE;\r
1882   dcb.fOutxDsrFlow = FALSE;\r
1883   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1884   dcb.fDsrSensitivity = FALSE;\r
1885   dcb.fTXContinueOnXoff = TRUE;\r
1886   dcb.fOutX = FALSE;\r
1887   dcb.fInX = FALSE;\r
1888   dcb.fNull = FALSE;\r
1889   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1890   dcb.fAbortOnError = FALSE;\r
1891   dcb.ByteSize = 7;\r
1892   dcb.Parity = SPACEPARITY;\r
1893   dcb.StopBits = ONESTOPBIT;\r
1894   settingsFileName = SETTINGS_FILE;\r
1895   saveSettingsOnExit = TRUE;\r
1896   boardX = CW_USEDEFAULT;\r
1897   boardY = CW_USEDEFAULT;\r
1898   consoleX = CW_USEDEFAULT; \r
1899   consoleY = CW_USEDEFAULT; \r
1900   consoleW = CW_USEDEFAULT;\r
1901   consoleH = CW_USEDEFAULT;\r
1902   analysisX = CW_USEDEFAULT; \r
1903   analysisY = CW_USEDEFAULT; \r
1904   analysisW = CW_USEDEFAULT;\r
1905   analysisH = CW_USEDEFAULT;\r
1906   commentX = CW_USEDEFAULT; \r
1907   commentY = CW_USEDEFAULT; \r
1908   commentW = CW_USEDEFAULT;\r
1909   commentH = CW_USEDEFAULT;\r
1910   editTagsX = CW_USEDEFAULT; \r
1911   editTagsY = CW_USEDEFAULT; \r
1912   editTagsW = CW_USEDEFAULT;\r
1913   editTagsH = CW_USEDEFAULT;\r
1914   gameListX = CW_USEDEFAULT; \r
1915   gameListY = CW_USEDEFAULT; \r
1916   gameListW = CW_USEDEFAULT;\r
1917   gameListH = CW_USEDEFAULT;\r
1918   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1919   icsNames = ICS_NAMES;\r
1920   firstChessProgramNames = FCP_NAMES;\r
1921   secondChessProgramNames = SCP_NAMES;\r
1922   appData.initialMode = "";\r
1923   appData.variant = "normal";\r
1924   appData.firstProtocolVersion = PROTOVER;\r
1925   appData.secondProtocolVersion = PROTOVER;\r
1926   appData.showButtonBar = TRUE;\r
1927 \r
1928    /* [AS] New properties (see comments in header file) */\r
1929   appData.firstScoreIsAbsolute = FALSE;\r
1930   appData.secondScoreIsAbsolute = FALSE;\r
1931   appData.saveExtendedInfoInPGN = FALSE;\r
1932   appData.hideThinkingFromHuman = FALSE;\r
1933   appData.liteBackTextureFile = "";\r
1934   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1935   appData.darkBackTextureFile = "";\r
1936   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1937   appData.renderPiecesWithFont = "";\r
1938   appData.fontToPieceTable = "";\r
1939   appData.fontBackColorWhite = 0;\r
1940   appData.fontForeColorWhite = 0;\r
1941   appData.fontBackColorBlack = 0;\r
1942   appData.fontForeColorBlack = 0;\r
1943   appData.fontPieceSize = 80;\r
1944   appData.overrideLineGap = 1;\r
1945   appData.adjudicateLossThreshold = 0;\r
1946   appData.delayBeforeQuit = 0;\r
1947   appData.delayAfterQuit = 0;\r
1948   appData.nameOfDebugFile = "winboard.debug";\r
1949   appData.pgnEventHeader = "Computer Chess Game";\r
1950   appData.defaultFrcPosition = -1;\r
1951   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1952   appData.saveOutOfBookInfo = TRUE;\r
1953   appData.showEvalInMoveHistory = TRUE;\r
1954   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1955   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1956   appData.highlightMoveWithArrow = FALSE;\r
1957   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1958   appData.useStickyWindows = TRUE;\r
1959   appData.adjudicateDrawMoves = 0;\r
1960   appData.autoDisplayComment = TRUE;\r
1961   appData.autoDisplayTags = TRUE;\r
1962   appData.firstIsUCI = FALSE;\r
1963   appData.secondIsUCI = FALSE;\r
1964   appData.firstHasOwnBookUCI = TRUE;\r
1965   appData.secondHasOwnBookUCI = TRUE;\r
1966   appData.polyglotDir = "";\r
1967   appData.usePolyglotBook = FALSE;\r
1968   appData.polyglotBook = "";\r
1969   appData.defaultHashSize = 64;\r
1970   appData.defaultCacheSizeEGTB = 4;\r
1971   appData.defaultPathEGTB = "c:\\egtb";\r
1972   appData.firstOptions = "";\r
1973   appData.secondOptions = "";\r
1974 \r
1975   InitWindowPlacement( &wpMoveHistory );\r
1976   InitWindowPlacement( &wpEvalGraph );\r
1977   InitWindowPlacement( &wpEngineOutput );\r
1978 \r
1979   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1980   appData.NrFiles      = -1;\r
1981   appData.NrRanks      = -1;\r
1982   appData.holdingsSize = -1;\r
1983   appData.testClaims   = FALSE;\r
1984   appData.checkMates   = FALSE;\r
1985   appData.materialDraws= FALSE;\r
1986   appData.trivialDraws = FALSE;\r
1987   appData.ruleMoves    = 51;\r
1988   appData.drawRepeats  = 6;\r
1989   appData.matchPause   = 10000;\r
1990   appData.alphaRank    = FALSE;\r
1991   appData.allWhite     = FALSE;\r
1992   appData.upsideDown   = FALSE;\r
1993   appData.serverPause  = 15;\r
1994   appData.serverMovesName   = NULL;\r
1995   appData.suppressLoadMoves = FALSE;\r
1996   appData.firstTimeOdds  = 1;\r
1997   appData.secondTimeOdds = 1;\r
1998   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1999   appData.secondAccumulateTC = 1;\r
2000   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2001   appData.secondNPS = -1;\r
2002   appData.engineComments = 1;\r
2003   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2004   appData.egtFormats = "";\r
2005 \r
2006 #ifdef ZIPPY\r
2007   appData.zippyTalk = ZIPPY_TALK;\r
2008   appData.zippyPlay = ZIPPY_PLAY;\r
2009   appData.zippyLines = ZIPPY_LINES;\r
2010   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2011   appData.zippyPassword = ZIPPY_PASSWORD;\r
2012   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2013   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2014   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2015   appData.zippyUseI = ZIPPY_USE_I;\r
2016   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2017   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2018   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2019   appData.zippyGameStart = ZIPPY_GAME_START;\r
2020   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2021   appData.zippyAbort = ZIPPY_ABORT;\r
2022   appData.zippyVariants = ZIPPY_VARIANTS;\r
2023   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2024   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2025 #endif\r
2026 \r
2027   /* Point font array elements to structures and\r
2028      parse default font names */\r
2029   for (i=0; i<NUM_FONTS; i++) {\r
2030     for (j=0; j<NUM_SIZES; j++) {\r
2031       font[j][i] = &fontRec[j][i];\r
2032       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2033     }\r
2034   }\r
2035   \r
2036   /* Parse default settings file if any */\r
2037   if (ParseSettingsFile(settingsFileName, buf)) {\r
2038     settingsFileName = strdup(buf);\r
2039   }\r
2040 \r
2041   /* Parse command line */\r
2042   ParseArgs(StringGet, &lpCmdLine);\r
2043 \r
2044   /* [HGM] make sure board size is acceptable */\r
2045   if(appData.NrFiles > BOARD_SIZE ||\r
2046      appData.NrRanks > BOARD_SIZE   )\r
2047       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2048 \r
2049   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2050    * with options from the command line, we now make an even higher priority\r
2051    * overrule by WB options attached to the engine command line. This so that\r
2052    * tournament managers can use WB options (such as /timeOdds) that follow\r
2053    * the engines.\r
2054    */\r
2055   if(appData.firstChessProgram != NULL) {\r
2056       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2057       static char *f = "first";\r
2058       char buf[MSG_SIZ], *q = buf;\r
2059       if(p != NULL) { // engine command line contains WinBoard options\r
2060           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2061           ParseArgs(StringGet, &q);\r
2062           p[-1] = 0; // cut them offengine command line\r
2063       }\r
2064   }\r
2065   // now do same for second chess program\r
2066   if(appData.secondChessProgram != NULL) {\r
2067       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2068       static char *s = "second";\r
2069       char buf[MSG_SIZ], *q = buf;\r
2070       if(p != NULL) { // engine command line contains WinBoard options\r
2071           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2072           ParseArgs(StringGet, &q);\r
2073           p[-1] = 0; // cut them offengine command line\r
2074       }\r
2075   }\r
2076 \r
2077 \r
2078   /* Propagate options that affect others */\r
2079   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2080   if (appData.icsActive || appData.noChessProgram) {\r
2081      chessProgram = FALSE;  /* not local chess program mode */\r
2082   }\r
2083 \r
2084   /* Open startup dialog if needed */\r
2085   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2086       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2087       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2088                         *appData.secondChessProgram == NULLCHAR))) {\r
2089     FARPROC lpProc;\r
2090     \r
2091     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2092     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2093     FreeProcInstance(lpProc);\r
2094   }\r
2095 \r
2096   /* Make sure save files land in the right (?) directory */\r
2097   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2098     appData.saveGameFile = strdup(buf);\r
2099   }\r
2100   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2101     appData.savePositionFile = strdup(buf);\r
2102   }\r
2103 \r
2104   /* Finish initialization for fonts and sounds */\r
2105   for (i=0; i<NUM_FONTS; i++) {\r
2106     for (j=0; j<NUM_SIZES; j++) {\r
2107       CreateFontInMF(font[j][i]);\r
2108     }\r
2109   }\r
2110   /* xboard, and older WinBoards, controlled the move sound with the\r
2111      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2112      always turn the option on (so that the backend will call us),\r
2113      then let the user turn the sound off by setting it to silence if\r
2114      desired.  To accommodate old winboard.ini files saved by old\r
2115      versions of WinBoard, we also turn off the sound if the option\r
2116      was initially set to false. */\r
2117   if (!appData.ringBellAfterMoves) {\r
2118     sounds[(int)SoundMove].name = strdup("");\r
2119     appData.ringBellAfterMoves = TRUE;\r
2120   }\r
2121   GetCurrentDirectory(MSG_SIZ, currDir);\r
2122   SetCurrentDirectory(installDir);\r
2123   LoadAllSounds();\r
2124   SetCurrentDirectory(currDir);\r
2125 \r
2126   p = icsTextMenuString;\r
2127   if (p[0] == '@') {\r
2128     FILE* f = fopen(p + 1, "r");\r
2129     if (f == NULL) {\r
2130       DisplayFatalError(p + 1, errno, 2);\r
2131       return;\r
2132     }\r
2133     i = fread(buf, 1, sizeof(buf)-1, f);\r
2134     fclose(f);\r
2135     buf[i] = NULLCHAR;\r
2136     p = buf;\r
2137   }\r
2138   ParseIcsTextMenu(strdup(p));\r
2139 }\r
2140 \r
2141 \r
2142 VOID\r
2143 InitMenuChecks()\r
2144 {\r
2145   HMENU hmenu = GetMenu(hwndMain);\r
2146 \r
2147   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2148                         MF_BYCOMMAND|((appData.icsActive &&\r
2149                                        *appData.icsCommPort != NULLCHAR) ?\r
2150                                       MF_ENABLED : MF_GRAYED));\r
2151   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2152                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2153                                      MF_CHECKED : MF_UNCHECKED));\r
2154 }\r
2155 \r
2156 \r
2157 VOID\r
2158 SaveSettings(char* name)\r
2159 {\r
2160   FILE *f;\r
2161   ArgDescriptor *ad;\r
2162   WINDOWPLACEMENT wp;\r
2163   char dir[MSG_SIZ];\r
2164 \r
2165   if (!hwndMain) return;\r
2166 \r
2167   GetCurrentDirectory(MSG_SIZ, dir);\r
2168   SetCurrentDirectory(installDir);\r
2169   f = fopen(name, "w");\r
2170   SetCurrentDirectory(dir);\r
2171   if (f == NULL) {\r
2172     DisplayError(name, errno);\r
2173     return;\r
2174   }\r
2175   fprintf(f, ";\n");\r
2176   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2177   fprintf(f, ";\n");\r
2178   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2179   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2180   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2181   fprintf(f, ";\n");\r
2182 \r
2183   wp.length = sizeof(WINDOWPLACEMENT);\r
2184   GetWindowPlacement(hwndMain, &wp);\r
2185   boardX = wp.rcNormalPosition.left;\r
2186   boardY = wp.rcNormalPosition.top;\r
2187 \r
2188   if (hwndConsole) {\r
2189     GetWindowPlacement(hwndConsole, &wp);\r
2190     consoleX = wp.rcNormalPosition.left;\r
2191     consoleY = wp.rcNormalPosition.top;\r
2192     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2193     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2194   }\r
2195 \r
2196   if (analysisDialog) {\r
2197     GetWindowPlacement(analysisDialog, &wp);\r
2198     analysisX = wp.rcNormalPosition.left;\r
2199     analysisY = wp.rcNormalPosition.top;\r
2200     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2201     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2202   }\r
2203 \r
2204   if (commentDialog) {\r
2205     GetWindowPlacement(commentDialog, &wp);\r
2206     commentX = wp.rcNormalPosition.left;\r
2207     commentY = wp.rcNormalPosition.top;\r
2208     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2209     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2210   }\r
2211 \r
2212   if (editTagsDialog) {\r
2213     GetWindowPlacement(editTagsDialog, &wp);\r
2214     editTagsX = wp.rcNormalPosition.left;\r
2215     editTagsY = wp.rcNormalPosition.top;\r
2216     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2217     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2218   }\r
2219 \r
2220   if (gameListDialog) {\r
2221     GetWindowPlacement(gameListDialog, &wp);\r
2222     gameListX = wp.rcNormalPosition.left;\r
2223     gameListY = wp.rcNormalPosition.top;\r
2224     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2225     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2226   }\r
2227 \r
2228   /* [AS] Move history */\r
2229   wpMoveHistory.visible = MoveHistoryIsUp();\r
2230   \r
2231   if( moveHistoryDialog ) {\r
2232     GetWindowPlacement(moveHistoryDialog, &wp);\r
2233     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2234     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2235     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2236     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2237   }\r
2238 \r
2239   /* [AS] Eval graph */\r
2240   wpEvalGraph.visible = EvalGraphIsUp();\r
2241 \r
2242   if( evalGraphDialog ) {\r
2243     GetWindowPlacement(evalGraphDialog, &wp);\r
2244     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2245     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2246     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2247     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2248   }\r
2249 \r
2250   /* [AS] Engine output */\r
2251   wpEngineOutput.visible = EngineOutputIsUp();\r
2252 \r
2253   if( engineOutputDialog ) {\r
2254     GetWindowPlacement(engineOutputDialog, &wp);\r
2255     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2256     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2257     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2258     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2259   }\r
2260 \r
2261   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2262     if (!ad->save) continue;\r
2263     switch (ad->argType) {\r
2264     case ArgString:\r
2265       {\r
2266         char *p = *(char **)ad->argLoc;\r
2267         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2268           /* Quote multiline values or \-containing values\r
2269              with { } if possible */\r
2270           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2271         } else {\r
2272           /* Else quote with " " */\r
2273           fprintf(f, "/%s=\"", ad->argName);\r
2274           while (*p) {\r
2275             if (*p == '\n') fprintf(f, "\n");\r
2276             else if (*p == '\r') fprintf(f, "\\r");\r
2277             else if (*p == '\t') fprintf(f, "\\t");\r
2278             else if (*p == '\b') fprintf(f, "\\b");\r
2279             else if (*p == '\f') fprintf(f, "\\f");\r
2280             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2281             else if (*p == '\"') fprintf(f, "\\\"");\r
2282             else if (*p == '\\') fprintf(f, "\\\\");\r
2283             else putc(*p, f);\r
2284             p++;\r
2285           }\r
2286           fprintf(f, "\"\n");\r
2287         }\r
2288       }\r
2289       break;\r
2290     case ArgInt:\r
2291       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2292       break;\r
2293     case ArgFloat:\r
2294       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2295       break;\r
2296     case ArgBoolean:\r
2297       fprintf(f, "/%s=%s\n", ad->argName, \r
2298         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2299       break;\r
2300     case ArgTrue:\r
2301       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2302       break;\r
2303     case ArgFalse:\r
2304       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2305       break;\r
2306     case ArgColor:\r
2307       {\r
2308         COLORREF color = *(COLORREF *)ad->argLoc;\r
2309         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2310           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2311       }\r
2312       break;\r
2313     case ArgAttribs:\r
2314       {\r
2315         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2316         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2317           (ta->effects & CFE_BOLD) ? "b" : "",\r
2318           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2319           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2320           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2321           (ta->effects) ? " " : "",\r
2322           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2323       }\r
2324       break;\r
2325     case ArgFilename:\r
2326       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2327         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2328       } else {\r
2329         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2330       }\r
2331       break;\r
2332     case ArgBoardSize:\r
2333       fprintf(f, "/%s=%s\n", ad->argName,\r
2334               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2335       break;\r
2336     case ArgFont:\r
2337       {\r
2338         int bs;\r
2339         for (bs=0; bs<NUM_SIZES; bs++) {\r
2340           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2341           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2342           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2343             ad->argName, mfp->faceName, mfp->pointSize,\r
2344             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2345             mfp->bold ? "b" : "",\r
2346             mfp->italic ? "i" : "",\r
2347             mfp->underline ? "u" : "",\r
2348             mfp->strikeout ? "s" : "");\r
2349         }\r
2350       }\r
2351       break;\r
2352     case ArgCommSettings:\r
2353       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2354     case ArgNone:\r
2355     case ArgSettingsFilename: ;\r
2356     }\r
2357   }\r
2358   fclose(f);\r
2359 }\r
2360 \r
2361 \r
2362 \r
2363 /*---------------------------------------------------------------------------*\\r
2364  *\r
2365  * GDI board drawing routines\r
2366  *\r
2367 \*---------------------------------------------------------------------------*/\r
2368 \r
2369 /* [AS] Draw square using background texture */\r
2370 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2371 {\r
2372     XFORM   x;\r
2373 \r
2374     if( mode == 0 ) {\r
2375         return; /* Should never happen! */\r
2376     }\r
2377 \r
2378     SetGraphicsMode( dst, GM_ADVANCED );\r
2379 \r
2380     switch( mode ) {\r
2381     case 1:\r
2382         /* Identity */\r
2383         break;\r
2384     case 2:\r
2385         /* X reflection */\r
2386         x.eM11 = -1.0;\r
2387         x.eM12 = 0;\r
2388         x.eM21 = 0;\r
2389         x.eM22 = 1.0;\r
2390         x.eDx = (FLOAT) dw + dx - 1;\r
2391         x.eDy = 0;\r
2392         dx = 0;\r
2393         SetWorldTransform( dst, &x );\r
2394         break;\r
2395     case 3:\r
2396         /* Y reflection */\r
2397         x.eM11 = 1.0;\r
2398         x.eM12 = 0;\r
2399         x.eM21 = 0;\r
2400         x.eM22 = -1.0;\r
2401         x.eDx = 0;\r
2402         x.eDy = (FLOAT) dh + dy - 1;\r
2403         dy = 0;\r
2404         SetWorldTransform( dst, &x );\r
2405         break;\r
2406     case 4:\r
2407         /* X/Y flip */\r
2408         x.eM11 = 0;\r
2409         x.eM12 = 1.0;\r
2410         x.eM21 = 1.0;\r
2411         x.eM22 = 0;\r
2412         x.eDx = (FLOAT) dx;\r
2413         x.eDy = (FLOAT) dy;\r
2414         dx = 0;\r
2415         dy = 0;\r
2416         SetWorldTransform( dst, &x );\r
2417         break;\r
2418     }\r
2419 \r
2420     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2421 \r
2422     x.eM11 = 1.0;\r
2423     x.eM12 = 0;\r
2424     x.eM21 = 0;\r
2425     x.eM22 = 1.0;\r
2426     x.eDx = 0;\r
2427     x.eDy = 0;\r
2428     SetWorldTransform( dst, &x );\r
2429 \r
2430     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2431 }\r
2432 \r
2433 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2434 enum {\r
2435     PM_WP = (int) WhitePawn, \r
2436     PM_WN = (int) WhiteKnight, \r
2437     PM_WB = (int) WhiteBishop, \r
2438     PM_WR = (int) WhiteRook, \r
2439     PM_WQ = (int) WhiteQueen, \r
2440     PM_WF = (int) WhiteFerz, \r
2441     PM_WW = (int) WhiteWazir, \r
2442     PM_WE = (int) WhiteAlfil, \r
2443     PM_WM = (int) WhiteMan, \r
2444     PM_WO = (int) WhiteCannon, \r
2445     PM_WU = (int) WhiteUnicorn, \r
2446     PM_WH = (int) WhiteNightrider, \r
2447     PM_WA = (int) WhiteAngel, \r
2448     PM_WC = (int) WhiteMarshall, \r
2449     PM_WAB = (int) WhiteCardinal, \r
2450     PM_WD = (int) WhiteDragon, \r
2451     PM_WL = (int) WhiteLance, \r
2452     PM_WS = (int) WhiteCobra, \r
2453     PM_WV = (int) WhiteFalcon, \r
2454     PM_WSG = (int) WhiteSilver, \r
2455     PM_WG = (int) WhiteGrasshopper, \r
2456     PM_WK = (int) WhiteKing,\r
2457     PM_BP = (int) BlackPawn, \r
2458     PM_BN = (int) BlackKnight, \r
2459     PM_BB = (int) BlackBishop, \r
2460     PM_BR = (int) BlackRook, \r
2461     PM_BQ = (int) BlackQueen, \r
2462     PM_BF = (int) BlackFerz, \r
2463     PM_BW = (int) BlackWazir, \r
2464     PM_BE = (int) BlackAlfil, \r
2465     PM_BM = (int) BlackMan,\r
2466     PM_BO = (int) BlackCannon, \r
2467     PM_BU = (int) BlackUnicorn, \r
2468     PM_BH = (int) BlackNightrider, \r
2469     PM_BA = (int) BlackAngel, \r
2470     PM_BC = (int) BlackMarshall, \r
2471     PM_BG = (int) BlackGrasshopper, \r
2472     PM_BAB = (int) BlackCardinal,\r
2473     PM_BD = (int) BlackDragon,\r
2474     PM_BL = (int) BlackLance,\r
2475     PM_BS = (int) BlackCobra,\r
2476     PM_BV = (int) BlackFalcon,\r
2477     PM_BSG = (int) BlackSilver,\r
2478     PM_BK = (int) BlackKing\r
2479 };\r
2480 \r
2481 static HFONT hPieceFont = NULL;\r
2482 static HBITMAP hPieceMask[(int) EmptySquare];\r
2483 static HBITMAP hPieceFace[(int) EmptySquare];\r
2484 static int fontBitmapSquareSize = 0;\r
2485 static char pieceToFontChar[(int) EmptySquare] =\r
2486                               { 'p', 'n', 'b', 'r', 'q', \r
2487                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2488                       'k', 'o', 'm', 'v', 't', 'w', \r
2489                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2490                                                               'l' };\r
2491 \r
2492 extern BOOL SetCharTable( char *table, const char * map );\r
2493 /* [HGM] moved to backend.c */\r
2494 \r
2495 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2496 {\r
2497     HBRUSH hbrush;\r
2498     BYTE r1 = GetRValue( color );\r
2499     BYTE g1 = GetGValue( color );\r
2500     BYTE b1 = GetBValue( color );\r
2501     BYTE r2 = r1 / 2;\r
2502     BYTE g2 = g1 / 2;\r
2503     BYTE b2 = b1 / 2;\r
2504     RECT rc;\r
2505 \r
2506     /* Create a uniform background first */\r
2507     hbrush = CreateSolidBrush( color );\r
2508     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2509     FillRect( hdc, &rc, hbrush );\r
2510     DeleteObject( hbrush );\r
2511     \r
2512     if( mode == 1 ) {\r
2513         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2514         int steps = squareSize / 2;\r
2515         int i;\r
2516 \r
2517         for( i=0; i<steps; i++ ) {\r
2518             BYTE r = r1 - (r1-r2) * i / steps;\r
2519             BYTE g = g1 - (g1-g2) * i / steps;\r
2520             BYTE b = b1 - (b1-b2) * i / steps;\r
2521 \r
2522             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2523             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2524             FillRect( hdc, &rc, hbrush );\r
2525             DeleteObject(hbrush);\r
2526         }\r
2527     }\r
2528     else if( mode == 2 ) {\r
2529         /* Diagonal gradient, good more or less for every piece */\r
2530         POINT triangle[3];\r
2531         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2532         HBRUSH hbrush_old;\r
2533         int steps = squareSize;\r
2534         int i;\r
2535 \r
2536         triangle[0].x = squareSize - steps;\r
2537         triangle[0].y = squareSize;\r
2538         triangle[1].x = squareSize;\r
2539         triangle[1].y = squareSize;\r
2540         triangle[2].x = squareSize;\r
2541         triangle[2].y = squareSize - steps;\r
2542 \r
2543         for( i=0; i<steps; i++ ) {\r
2544             BYTE r = r1 - (r1-r2) * i / steps;\r
2545             BYTE g = g1 - (g1-g2) * i / steps;\r
2546             BYTE b = b1 - (b1-b2) * i / steps;\r
2547 \r
2548             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2549             hbrush_old = SelectObject( hdc, hbrush );\r
2550             Polygon( hdc, triangle, 3 );\r
2551             SelectObject( hdc, hbrush_old );\r
2552             DeleteObject(hbrush);\r
2553             triangle[0].x++;\r
2554             triangle[2].y++;\r
2555         }\r
2556 \r
2557         SelectObject( hdc, hpen );\r
2558     }\r
2559 }\r
2560 \r
2561 /*\r
2562     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2563     seems to work ok. The main problem here is to find the "inside" of a chess\r
2564     piece: follow the steps as explained below.\r
2565 */\r
2566 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2567 {\r
2568     HBITMAP hbm;\r
2569     HBITMAP hbm_old;\r
2570     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2571     RECT rc;\r
2572     SIZE sz;\r
2573     POINT pt;\r
2574     int backColor = whitePieceColor; \r
2575     int foreColor = blackPieceColor;\r
2576     \r
2577     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2578         backColor = appData.fontBackColorWhite;\r
2579         foreColor = appData.fontForeColorWhite;\r
2580     }\r
2581     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2582         backColor = appData.fontBackColorBlack;\r
2583         foreColor = appData.fontForeColorBlack;\r
2584     }\r
2585 \r
2586     /* Mask */\r
2587     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2588 \r
2589     hbm_old = SelectObject( hdc, hbm );\r
2590 \r
2591     rc.left = 0;\r
2592     rc.top = 0;\r
2593     rc.right = squareSize;\r
2594     rc.bottom = squareSize;\r
2595 \r
2596     /* Step 1: background is now black */\r
2597     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2598 \r
2599     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2600 \r
2601     pt.x = (squareSize - sz.cx) / 2;\r
2602     pt.y = (squareSize - sz.cy) / 2;\r
2603 \r
2604     SetBkMode( hdc, TRANSPARENT );\r
2605     SetTextColor( hdc, chroma );\r
2606     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2607     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2608 \r
2609     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2610     /* Step 3: the area outside the piece is filled with white */\r
2611 //    FloodFill( hdc, 0, 0, chroma );\r
2612     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2613     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2614     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2615     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2616     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2617     /* \r
2618         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2619         but if the start point is not inside the piece we're lost!\r
2620         There should be a better way to do this... if we could create a region or path\r
2621         from the fill operation we would be fine for example.\r
2622     */\r
2623 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2624     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2625 \r
2626     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2627         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2628         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2629 \r
2630         SelectObject( dc2, bm2 );\r
2631         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2632         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2634         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2635         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2636 \r
2637         DeleteDC( dc2 );\r
2638         DeleteObject( bm2 );\r
2639     }\r
2640 \r
2641     SetTextColor( hdc, 0 );\r
2642     /* \r
2643         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2644         draw the piece again in black for safety.\r
2645     */\r
2646     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2647 \r
2648     SelectObject( hdc, hbm_old );\r
2649 \r
2650     if( hPieceMask[index] != NULL ) {\r
2651         DeleteObject( hPieceMask[index] );\r
2652     }\r
2653 \r
2654     hPieceMask[index] = hbm;\r
2655 \r
2656     /* Face */\r
2657     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2658 \r
2659     SelectObject( hdc, hbm );\r
2660 \r
2661     {\r
2662         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2663         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2664         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2665 \r
2666         SelectObject( dc1, hPieceMask[index] );\r
2667         SelectObject( dc2, bm2 );\r
2668         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2669         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2670         \r
2671         /* \r
2672             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2673             the piece background and deletes (makes transparent) the rest.\r
2674             Thanks to that mask, we are free to paint the background with the greates\r
2675             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2676             We use this, to make gradients and give the pieces a "roundish" look.\r
2677         */\r
2678         SetPieceBackground( hdc, backColor, 2 );\r
2679         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2680 \r
2681         DeleteDC( dc2 );\r
2682         DeleteDC( dc1 );\r
2683         DeleteObject( bm2 );\r
2684     }\r
2685 \r
2686     SetTextColor( hdc, foreColor );\r
2687     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2688 \r
2689     SelectObject( hdc, hbm_old );\r
2690 \r
2691     if( hPieceFace[index] != NULL ) {\r
2692         DeleteObject( hPieceFace[index] );\r
2693     }\r
2694 \r
2695     hPieceFace[index] = hbm;\r
2696 }\r
2697 \r
2698 static int TranslatePieceToFontPiece( int piece )\r
2699 {\r
2700     switch( piece ) {\r
2701     case BlackPawn:\r
2702         return PM_BP;\r
2703     case BlackKnight:\r
2704         return PM_BN;\r
2705     case BlackBishop:\r
2706         return PM_BB;\r
2707     case BlackRook:\r
2708         return PM_BR;\r
2709     case BlackQueen:\r
2710         return PM_BQ;\r
2711     case BlackKing:\r
2712         return PM_BK;\r
2713     case WhitePawn:\r
2714         return PM_WP;\r
2715     case WhiteKnight:\r
2716         return PM_WN;\r
2717     case WhiteBishop:\r
2718         return PM_WB;\r
2719     case WhiteRook:\r
2720         return PM_WR;\r
2721     case WhiteQueen:\r
2722         return PM_WQ;\r
2723     case WhiteKing:\r
2724         return PM_WK;\r
2725 \r
2726     case BlackAngel:\r
2727         return PM_BA;\r
2728     case BlackMarshall:\r
2729         return PM_BC;\r
2730     case BlackFerz:\r
2731         return PM_BF;\r
2732     case BlackNightrider:\r
2733         return PM_BH;\r
2734     case BlackAlfil:\r
2735         return PM_BE;\r
2736     case BlackWazir:\r
2737         return PM_BW;\r
2738     case BlackUnicorn:\r
2739         return PM_BU;\r
2740     case BlackCannon:\r
2741         return PM_BO;\r
2742     case BlackGrasshopper:\r
2743         return PM_BG;\r
2744     case BlackMan:\r
2745         return PM_BM;\r
2746     case BlackSilver:\r
2747         return PM_BSG;\r
2748     case BlackLance:\r
2749         return PM_BL;\r
2750     case BlackFalcon:\r
2751         return PM_BV;\r
2752     case BlackCobra:\r
2753         return PM_BS;\r
2754     case BlackCardinal:\r
2755         return PM_BAB;\r
2756     case BlackDragon:\r
2757         return PM_BD;\r
2758 \r
2759     case WhiteAngel:\r
2760         return PM_WA;\r
2761     case WhiteMarshall:\r
2762         return PM_WC;\r
2763     case WhiteFerz:\r
2764         return PM_WF;\r
2765     case WhiteNightrider:\r
2766         return PM_WH;\r
2767     case WhiteAlfil:\r
2768         return PM_WE;\r
2769     case WhiteWazir:\r
2770         return PM_WW;\r
2771     case WhiteUnicorn:\r
2772         return PM_WU;\r
2773     case WhiteCannon:\r
2774         return PM_WO;\r
2775     case WhiteGrasshopper:\r
2776         return PM_WG;\r
2777     case WhiteMan:\r
2778         return PM_WM;\r
2779     case WhiteSilver:\r
2780         return PM_WSG;\r
2781     case WhiteLance:\r
2782         return PM_WL;\r
2783     case WhiteFalcon:\r
2784         return PM_WV;\r
2785     case WhiteCobra:\r
2786         return PM_WS;\r
2787     case WhiteCardinal:\r
2788         return PM_WAB;\r
2789     case WhiteDragon:\r
2790         return PM_WD;\r
2791     }\r
2792 \r
2793     return 0;\r
2794 }\r
2795 \r
2796 void CreatePiecesFromFont()\r
2797 {\r
2798     LOGFONT lf;\r
2799     HDC hdc_window = NULL;\r
2800     HDC hdc = NULL;\r
2801     HFONT hfont_old;\r
2802     int fontHeight;\r
2803     int i;\r
2804 \r
2805     if( fontBitmapSquareSize < 0 ) {\r
2806         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2807         return;\r
2808     }\r
2809 \r
2810     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2811         fontBitmapSquareSize = -1;\r
2812         return;\r
2813     }\r
2814 \r
2815     if( fontBitmapSquareSize != squareSize ) {\r
2816         hdc_window = GetDC( hwndMain );\r
2817         hdc = CreateCompatibleDC( hdc_window );\r
2818 \r
2819         if( hPieceFont != NULL ) {\r
2820             DeleteObject( hPieceFont );\r
2821         }\r
2822         else {\r
2823             for( i=0; i<=(int)BlackKing; i++ ) {\r
2824                 hPieceMask[i] = NULL;\r
2825                 hPieceFace[i] = NULL;\r
2826             }\r
2827         }\r
2828 \r
2829         fontHeight = 75;\r
2830 \r
2831         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2832             fontHeight = appData.fontPieceSize;\r
2833         }\r
2834 \r
2835         fontHeight = (fontHeight * squareSize) / 100;\r
2836 \r
2837         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2838         lf.lfWidth = 0;\r
2839         lf.lfEscapement = 0;\r
2840         lf.lfOrientation = 0;\r
2841         lf.lfWeight = FW_NORMAL;\r
2842         lf.lfItalic = 0;\r
2843         lf.lfUnderline = 0;\r
2844         lf.lfStrikeOut = 0;\r
2845         lf.lfCharSet = DEFAULT_CHARSET;\r
2846         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2847         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2848         lf.lfQuality = PROOF_QUALITY;\r
2849         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2850         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2851         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2852 \r
2853         hPieceFont = CreateFontIndirect( &lf );\r
2854 \r
2855         if( hPieceFont == NULL ) {\r
2856             fontBitmapSquareSize = -2;\r
2857         }\r
2858         else {\r
2859             /* Setup font-to-piece character table */\r
2860             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2861                 /* No (or wrong) global settings, try to detect the font */\r
2862                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2863                     /* Alpha */\r
2864                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2865                 }\r
2866                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2867                     /* DiagramTT* family */\r
2868                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2869                 }\r
2870                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2871                     /* Fairy symbols */\r
2872                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2873                 }\r
2874                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2875                     /* Good Companion (Some characters get warped as literal :-( */\r
2876                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2877                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2878                     SetCharTable(pieceToFontChar, s);\r
2879                 }\r
2880                 else {\r
2881                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2882                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2883                 }\r
2884             }\r
2885 \r
2886             /* Create bitmaps */\r
2887             hfont_old = SelectObject( hdc, hPieceFont );\r
2888 #if 0\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2901 \r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2933             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2934 #else\r
2935             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2936                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2937                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2938 #endif\r
2939             SelectObject( hdc, hfont_old );\r
2940 \r
2941             fontBitmapSquareSize = squareSize;\r
2942         }\r
2943     }\r
2944 \r
2945     if( hdc != NULL ) {\r
2946         DeleteDC( hdc );\r
2947     }\r
2948 \r
2949     if( hdc_window != NULL ) {\r
2950         ReleaseDC( hwndMain, hdc_window );\r
2951     }\r
2952 }\r
2953 \r
2954 HBITMAP\r
2955 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2956 {\r
2957   char name[128];\r
2958 \r
2959   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2960   if (gameInfo.event &&\r
2961       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2962       strcmp(name, "k80s") == 0) {\r
2963     strcpy(name, "tim");\r
2964   }\r
2965   return LoadBitmap(hinst, name);\r
2966 }\r
2967 \r
2968 \r
2969 /* Insert a color into the program's logical palette\r
2970    structure.  This code assumes the given color is\r
2971    the result of the RGB or PALETTERGB macro, and it\r
2972    knows how those macros work (which is documented).\r
2973 */\r
2974 VOID\r
2975 InsertInPalette(COLORREF color)\r
2976 {\r
2977   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2978 \r
2979   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2980     DisplayFatalError("Too many colors", 0, 1);\r
2981     pLogPal->palNumEntries--;\r
2982     return;\r
2983   }\r
2984 \r
2985   pe->peFlags = (char) 0;\r
2986   pe->peRed = (char) (0xFF & color);\r
2987   pe->peGreen = (char) (0xFF & (color >> 8));\r
2988   pe->peBlue = (char) (0xFF & (color >> 16));\r
2989   return;\r
2990 }\r
2991 \r
2992 \r
2993 VOID\r
2994 InitDrawingColors()\r
2995 {\r
2996   if (pLogPal == NULL) {\r
2997     /* Allocate enough memory for a logical palette with\r
2998      * PALETTESIZE entries and set the size and version fields\r
2999      * of the logical palette structure.\r
3000      */\r
3001     pLogPal = (NPLOGPALETTE)\r
3002       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3003                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3004     pLogPal->palVersion    = 0x300;\r
3005   }\r
3006   pLogPal->palNumEntries = 0;\r
3007 \r
3008   InsertInPalette(lightSquareColor);\r
3009   InsertInPalette(darkSquareColor);\r
3010   InsertInPalette(whitePieceColor);\r
3011   InsertInPalette(blackPieceColor);\r
3012   InsertInPalette(highlightSquareColor);\r
3013   InsertInPalette(premoveHighlightColor);\r
3014 \r
3015   /*  create a logical color palette according the information\r
3016    *  in the LOGPALETTE structure.\r
3017    */\r
3018   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3019 \r
3020   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3021   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3022   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3023   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3024   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3025   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3026   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3027   /* [AS] Force rendering of the font-based pieces */\r
3028   if( fontBitmapSquareSize > 0 ) {\r
3029     fontBitmapSquareSize = 0;\r
3030   }\r
3031 }\r
3032 \r
3033 \r
3034 int\r
3035 BoardWidth(int boardSize, int n)\r
3036 { /* [HGM] argument n added to allow different width and height */\r
3037   int lineGap = sizeInfo[boardSize].lineGap;\r
3038 \r
3039   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3040       lineGap = appData.overrideLineGap;\r
3041   }\r
3042 \r
3043   return (n + 1) * lineGap +\r
3044           n * sizeInfo[boardSize].squareSize;\r
3045 }\r
3046 \r
3047 /* Respond to board resize by dragging edge */\r
3048 VOID\r
3049 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3050 {\r
3051   BoardSize newSize = NUM_SIZES - 1;\r
3052   static int recurse = 0;\r
3053   if (IsIconic(hwndMain)) return;\r
3054   if (recurse > 0) return;\r
3055   recurse++;\r
3056   while (newSize > 0) {\r
3057         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3058         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3059            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3060     newSize--;\r
3061   } \r
3062   boardSize = newSize;\r
3063   InitDrawingSizes(boardSize, flags);\r
3064   recurse--;\r
3065 }\r
3066 \r
3067 \r
3068 \r
3069 VOID\r
3070 InitDrawingSizes(BoardSize boardSize, int flags)\r
3071 {\r
3072   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3073   ChessSquare piece;\r
3074   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3075   HDC hdc;\r
3076   SIZE clockSize, messageSize;\r
3077   HFONT oldFont;\r
3078   char buf[MSG_SIZ];\r
3079   char *str;\r
3080   HMENU hmenu = GetMenu(hwndMain);\r
3081   RECT crect, wrect;\r
3082   int offby;\r
3083   LOGBRUSH logbrush;\r
3084 \r
3085   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3086   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3087 \r
3088   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3089   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3090 \r
3091   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3092   smallLayout = sizeInfo[boardSize].smallLayout;\r
3093   squareSize = sizeInfo[boardSize].squareSize;\r
3094   lineGap = sizeInfo[boardSize].lineGap;\r
3095   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3096 \r
3097   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3098       lineGap = appData.overrideLineGap;\r
3099   }\r
3100 \r
3101   if (tinyLayout != oldTinyLayout) {\r
3102     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3103     if (tinyLayout) {\r
3104       style &= ~WS_SYSMENU;\r
3105       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3106                  "&Minimize\tCtrl+F4");\r
3107     } else {\r
3108       style |= WS_SYSMENU;\r
3109       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3110     }\r
3111     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3112 \r
3113     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3114       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3115         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3116     }\r
3117     DrawMenuBar(hwndMain);\r
3118   }\r
3119 \r
3120   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3121   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3122 \r
3123   /* Get text area sizes */\r
3124   hdc = GetDC(hwndMain);\r
3125   if (appData.clockMode) {\r
3126     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3127   } else {\r
3128     sprintf(buf, "White");\r
3129   }\r
3130   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3131   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3132   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3133   str = "We only care about the height here";\r
3134   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3135   SelectObject(hdc, oldFont);\r
3136   ReleaseDC(hwndMain, hdc);\r
3137 \r
3138   /* Compute where everything goes */\r
3139   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3140         /* [HGM] logo: if either logo is on, reserve space for it */\r
3141         logoHeight =  2*clockSize.cy;\r
3142         leftLogoRect.left   = OUTER_MARGIN;\r
3143         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3144         leftLogoRect.top    = OUTER_MARGIN;\r
3145         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3146 \r
3147         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3148         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3149         rightLogoRect.top    = OUTER_MARGIN;\r
3150         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3151 \r
3152 \r
3153     whiteRect.left = leftLogoRect.right;\r
3154     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3155     whiteRect.top = OUTER_MARGIN;\r
3156     whiteRect.bottom = whiteRect.top + logoHeight;\r
3157 \r
3158     blackRect.right = rightLogoRect.left;\r
3159     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3160     blackRect.top = whiteRect.top;\r
3161     blackRect.bottom = whiteRect.bottom;\r
3162   } else {\r
3163     whiteRect.left = OUTER_MARGIN;\r
3164     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3165     whiteRect.top = OUTER_MARGIN;\r
3166     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3167 \r
3168     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3169     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3170     blackRect.top = whiteRect.top;\r
3171     blackRect.bottom = whiteRect.bottom;\r
3172   }\r
3173 \r
3174   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3175   if (appData.showButtonBar) {\r
3176     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3177       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3178   } else {\r
3179     messageRect.right = OUTER_MARGIN + boardWidth;\r
3180   }\r
3181   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3182   messageRect.bottom = messageRect.top + messageSize.cy;\r
3183 \r
3184   boardRect.left = OUTER_MARGIN;\r
3185   boardRect.right = boardRect.left + boardWidth;\r
3186   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3187   boardRect.bottom = boardRect.top + boardHeight;\r
3188 \r
3189   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3190   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3191   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3192   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3193   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3194     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3195   GetWindowRect(hwndMain, &wrect);\r
3196   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3197                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3198   /* compensate if menu bar wrapped */\r
3199   GetClientRect(hwndMain, &crect);\r
3200   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3201   winHeight += offby;\r
3202   switch (flags) {\r
3203   case WMSZ_TOPLEFT:\r
3204     SetWindowPos(hwndMain, NULL, \r
3205                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3206                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3207     break;\r
3208 \r
3209   case WMSZ_TOPRIGHT:\r
3210   case WMSZ_TOP:\r
3211     SetWindowPos(hwndMain, NULL, \r
3212                  wrect.left, wrect.bottom - winHeight, \r
3213                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3214     break;\r
3215 \r
3216   case WMSZ_BOTTOMLEFT:\r
3217   case WMSZ_LEFT:\r
3218     SetWindowPos(hwndMain, NULL, \r
3219                  wrect.right - winWidth, wrect.top, \r
3220                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3221     break;\r
3222 \r
3223   case WMSZ_BOTTOMRIGHT:\r
3224   case WMSZ_BOTTOM:\r
3225   case WMSZ_RIGHT:\r
3226   default:\r
3227     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3228                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3229     break;\r
3230   }\r
3231 \r
3232   hwndPause = NULL;\r
3233   for (i = 0; i < N_BUTTONS; i++) {\r
3234     if (buttonDesc[i].hwnd != NULL) {\r
3235       DestroyWindow(buttonDesc[i].hwnd);\r
3236       buttonDesc[i].hwnd = NULL;\r
3237     }\r
3238     if (appData.showButtonBar) {\r
3239       buttonDesc[i].hwnd =\r
3240         CreateWindow("BUTTON", buttonDesc[i].label,\r
3241                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3242                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3243                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3244                      (HMENU) buttonDesc[i].id,\r
3245                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3246       if (tinyLayout) {\r
3247         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3248                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3249                     MAKELPARAM(FALSE, 0));\r
3250       }\r
3251       if (buttonDesc[i].id == IDM_Pause)\r
3252         hwndPause = buttonDesc[i].hwnd;\r
3253       buttonDesc[i].wndproc = (WNDPROC)\r
3254         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3255     }\r
3256   }\r
3257   if (gridPen != NULL) DeleteObject(gridPen);\r
3258   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3259   if (premovePen != NULL) DeleteObject(premovePen);\r
3260   if (lineGap != 0) {\r
3261     logbrush.lbStyle = BS_SOLID;\r
3262     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3263     gridPen =\r
3264       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3265                    lineGap, &logbrush, 0, NULL);\r
3266     logbrush.lbColor = highlightSquareColor;\r
3267     highlightPen =\r
3268       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3269                    lineGap, &logbrush, 0, NULL);\r
3270 \r
3271     logbrush.lbColor = premoveHighlightColor; \r
3272     premovePen =\r
3273       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3274                    lineGap, &logbrush, 0, NULL);\r
3275 \r
3276     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3277     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3278       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3279       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3280         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3281       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3282         BOARD_WIDTH * (squareSize + lineGap);\r
3283       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3284     }\r
3285     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3286       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3287       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3288         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3289         lineGap / 2 + (i * (squareSize + lineGap));\r
3290       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3291         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3292       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3293     }\r
3294   }\r
3295 \r
3296   /* [HGM] Licensing requirement */\r
3297 #ifdef GOTHIC\r
3298   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3299 #endif\r
3300 #ifdef FALCON\r
3301   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3302 #endif\r
3303   GothicPopUp( "", VariantNormal);\r
3304 \r
3305 \r
3306 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3307   oldBoardSize = boardSize;\r
3308   oldTinyLayout = tinyLayout;\r
3309 \r
3310   /* Load piece bitmaps for this board size */\r
3311   for (i=0; i<=2; i++) {\r
3312     for (piece = WhitePawn;\r
3313          (int) piece < (int) BlackPawn;\r
3314          piece = (ChessSquare) ((int) piece + 1)) {\r
3315       if (pieceBitmap[i][piece] != NULL)\r
3316         DeleteObject(pieceBitmap[i][piece]);\r
3317     }\r
3318   }\r
3319 \r
3320   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3321   // Orthodox Chess pieces\r
3322   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3323   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3324   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3325   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3326   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3327   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3328   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3329   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3330   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3331   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3332   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3333   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3334   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3335   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3336   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3337   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3338     // in Shogi, Hijack the unused Queen for Lance\r
3339     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3340     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3341     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3342   } else {\r
3343     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3344     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3345     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3346   }\r
3347 \r
3348   if(squareSize <= 72 && squareSize >= 33) { \r
3349     /* A & C are available in most sizes now */\r
3350     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3351       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3352       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3353       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3354       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3355       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3356       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3357       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3358       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3359       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3360       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3361       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3362       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3363     } else { // Smirf-like\r
3364       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3365       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3366       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3367     }\r
3368     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3369       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3370       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3371       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3372     } else { // WinBoard standard\r
3373       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3374       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3375       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3376     }\r
3377   }\r
3378 \r
3379 \r
3380   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3381     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3382     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3383     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3384     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3385     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3386     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3387     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3388     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3389     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3390     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3391     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3392     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3393     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3394     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3395     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3396     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3397     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3398     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3399     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3400     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3401     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3402     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3403     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3404     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3405     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3406     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3407     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3408     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3409     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3410     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3411 \r
3412     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3413       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3414       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3415       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3416       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3417       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3418       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3419       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3420       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3421       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3422       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3423       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3424       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3425     } else {\r
3426       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3427       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3428       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3429       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3430       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3431       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3432       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3433       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3434       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3435       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3436       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3437       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3438     }\r
3439 \r
3440   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3441     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3442     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3443     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3444     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3445     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3446     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3447     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3448     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3449     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3450     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3451     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3452     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3453     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3454     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3455   }\r
3456 \r
3457 \r
3458   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3459   /* special Shogi support in this size */\r
3460   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3461       for (piece = WhitePawn;\r
3462            (int) piece < (int) BlackPawn;\r
3463            piece = (ChessSquare) ((int) piece + 1)) {\r
3464         if (pieceBitmap[i][piece] != NULL)\r
3465           DeleteObject(pieceBitmap[i][piece]);\r
3466       }\r
3467     }\r
3468   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3469   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3470   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3471   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3472   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3474   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3475   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3476   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3477   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3478   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3479   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3480   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3481   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3482   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3483   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3484   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3485   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3486   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3487   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3488   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3489   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3490   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3491   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3492   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3493   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3494   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3495   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3496   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3497   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3498   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3499   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3500   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3501   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3502   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3503   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3504   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3505   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3506   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3507   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3508   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3509   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3510   minorSize = 0;\r
3511   }\r
3512 }\r
3513 \r
3514 HBITMAP\r
3515 PieceBitmap(ChessSquare p, int kind)\r
3516 {\r
3517   if ((int) p >= (int) BlackPawn)\r
3518     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3519 \r
3520   return pieceBitmap[kind][(int) p];\r
3521 }\r
3522 \r
3523 /***************************************************************/\r
3524 \r
3525 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3526 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3527 /*\r
3528 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3529 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3530 */\r
3531 \r
3532 VOID\r
3533 SquareToPos(int row, int column, int * x, int * y)\r
3534 {\r
3535   if (flipView) {\r
3536     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3537     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3538   } else {\r
3539     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3540     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3541   }\r
3542 }\r
3543 \r
3544 VOID\r
3545 DrawCoordsOnDC(HDC hdc)\r
3546 {\r
3547   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
3548   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
3549   char str[2] = { NULLCHAR, NULLCHAR };\r
3550   int oldMode, oldAlign, x, y, start, i;\r
3551   HFONT oldFont;\r
3552   HBRUSH oldBrush;\r
3553 \r
3554   if (!appData.showCoords)\r
3555     return;\r
3556 \r
3557   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3558 \r
3559   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3560   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3561   oldAlign = GetTextAlign(hdc);\r
3562   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3563 \r
3564   y = boardRect.top + lineGap;\r
3565   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3566 \r
3567   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3568   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3569     str[0] = files[start + i];\r
3570     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3571     y += squareSize + lineGap;\r
3572   }\r
3573 \r
3574   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3575 \r
3576   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3577   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3578     str[0] = ranks[start + i];\r
3579     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3580     x += squareSize + lineGap;\r
3581   }    \r
3582 \r
3583   SelectObject(hdc, oldBrush);\r
3584   SetBkMode(hdc, oldMode);\r
3585   SetTextAlign(hdc, oldAlign);\r
3586   SelectObject(hdc, oldFont);\r
3587 }\r
3588 \r
3589 VOID\r
3590 DrawGridOnDC(HDC hdc)\r
3591 {\r
3592   HPEN oldPen;\r
3593  \r
3594   if (lineGap != 0) {\r
3595     oldPen = SelectObject(hdc, gridPen);\r
3596     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3597     SelectObject(hdc, oldPen);\r
3598   }\r
3599 }\r
3600 \r
3601 #define HIGHLIGHT_PEN 0\r
3602 #define PREMOVE_PEN   1\r
3603 \r
3604 VOID\r
3605 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3606 {\r
3607   int x1, y1;\r
3608   HPEN oldPen, hPen;\r
3609   if (lineGap == 0) return;\r
3610   if (flipView) {\r
3611     x1 = boardRect.left +\r
3612       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3613     y1 = boardRect.top +\r
3614       lineGap/2 + y * (squareSize + lineGap);\r
3615   } else {\r
3616     x1 = boardRect.left +\r
3617       lineGap/2 + x * (squareSize + lineGap);\r
3618     y1 = boardRect.top +\r
3619       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3620   }\r
3621   hPen = pen ? premovePen : highlightPen;\r
3622   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3623   MoveToEx(hdc, x1, y1, NULL);\r
3624   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3625   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3626   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3627   LineTo(hdc, x1, y1);\r
3628   SelectObject(hdc, oldPen);\r
3629 }\r
3630 \r
3631 VOID\r
3632 DrawHighlightsOnDC(HDC hdc)\r
3633 {\r
3634   int i;\r
3635   for (i=0; i<2; i++) {\r
3636     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3637       DrawHighlightOnDC(hdc, TRUE,\r
3638                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3639                         HIGHLIGHT_PEN);\r
3640   }\r
3641   for (i=0; i<2; i++) {\r
3642     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3643         premoveHighlightInfo.sq[i].y >= 0) {\r
3644         DrawHighlightOnDC(hdc, TRUE,\r
3645                           premoveHighlightInfo.sq[i].x, \r
3646                           premoveHighlightInfo.sq[i].y,\r
3647                           PREMOVE_PEN);\r
3648     }\r
3649   }\r
3650 }\r
3651 \r
3652 /* Note: sqcolor is used only in monoMode */\r
3653 /* Note that this code is largely duplicated in woptions.c,\r
3654    function DrawSampleSquare, so that needs to be updated too */\r
3655 VOID\r
3656 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3657 {\r
3658   HBITMAP oldBitmap;\r
3659   HBRUSH oldBrush;\r
3660   int tmpSize;\r
3661 \r
3662   if (appData.blindfold) return;\r
3663 \r
3664   /* [AS] Use font-based pieces if needed */\r
3665   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3666     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3667     CreatePiecesFromFont();\r
3668 \r
3669     if( fontBitmapSquareSize == squareSize ) {\r
3670         int index = TranslatePieceToFontPiece(piece);\r
3671 \r
3672         SelectObject( tmphdc, hPieceMask[ index ] );\r
3673 \r
3674         BitBlt( hdc,\r
3675             x, y,\r
3676             squareSize, squareSize,\r
3677             tmphdc,\r
3678             0, 0,\r
3679             SRCAND );\r
3680 \r
3681         SelectObject( tmphdc, hPieceFace[ index ] );\r
3682 \r
3683         BitBlt( hdc,\r
3684             x, y,\r
3685             squareSize, squareSize,\r
3686             tmphdc,\r
3687             0, 0,\r
3688             SRCPAINT );\r
3689 \r
3690         return;\r
3691     }\r
3692   }\r
3693 \r
3694   if (appData.monoMode) {\r
3695     SelectObject(tmphdc, PieceBitmap(piece, \r
3696       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3697     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3698            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3699   } else {\r
3700     tmpSize = squareSize;\r
3701     if(minorSize &&\r
3702         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3703          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3704       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3705       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3706       x += (squareSize - minorSize)>>1;\r
3707       y += squareSize - minorSize - 2;\r
3708       tmpSize = minorSize;\r
3709     }\r
3710     if (color || appData.allWhite ) {\r
3711       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3712       if( color )\r
3713               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3714       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3715       if(appData.upsideDown && color==flipView)\r
3716         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3717       else\r
3718         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3719 #if 0\r
3720       /* Use black piece color for outline of white pieces */\r
3721       /* Not sure this looks really good (though xboard does it).\r
3722          Maybe better to have another selectable color, default black */\r
3723       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3724       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3725       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3726 #else\r
3727       /* Use black for outline of white pieces */\r
3728       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3729       if(appData.upsideDown && color==flipView)\r
3730         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3731       else\r
3732         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3733 #endif\r
3734     } else {\r
3735 #if 0\r
3736       /* Use white piece color for details of black pieces */\r
3737       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3738          WHITE_PIECE ones aren't always the right shape. */\r
3739       /* Not sure this looks really good (though xboard does it).\r
3740          Maybe better to have another selectable color, default medium gray? */\r
3741       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3742       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3743       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3745       SelectObject(hdc, blackPieceBrush);\r
3746       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3747 #else\r
3748       /* Use square color for details of black pieces */\r
3749       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3750       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3751       if(appData.upsideDown && !flipView)\r
3752         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3753       else\r
3754         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3755 #endif\r
3756     }\r
3757     SelectObject(hdc, oldBrush);\r
3758     SelectObject(tmphdc, oldBitmap);\r
3759   }\r
3760 }\r
3761 \r
3762 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3763 int GetBackTextureMode( int algo )\r
3764 {\r
3765     int result = BACK_TEXTURE_MODE_DISABLED;\r
3766 \r
3767     switch( algo ) \r
3768     {\r
3769         case BACK_TEXTURE_MODE_PLAIN:\r
3770             result = 1; /* Always use identity map */\r
3771             break;\r
3772         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3773             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3774             break;\r
3775     }\r
3776 \r
3777     return result;\r
3778 }\r
3779 \r
3780 /* \r
3781     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3782     to handle redraws cleanly (as random numbers would always be different).\r
3783 */\r
3784 VOID RebuildTextureSquareInfo()\r
3785 {\r
3786     BITMAP bi;\r
3787     int lite_w = 0;\r
3788     int lite_h = 0;\r
3789     int dark_w = 0;\r
3790     int dark_h = 0;\r
3791     int row;\r
3792     int col;\r
3793 \r
3794     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3795 \r
3796     if( liteBackTexture != NULL ) {\r
3797         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3798             lite_w = bi.bmWidth;\r
3799             lite_h = bi.bmHeight;\r
3800         }\r
3801     }\r
3802 \r
3803     if( darkBackTexture != NULL ) {\r
3804         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3805             dark_w = bi.bmWidth;\r
3806             dark_h = bi.bmHeight;\r
3807         }\r
3808     }\r
3809 \r
3810     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3811         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3812             if( (col + row) & 1 ) {\r
3813                 /* Lite square */\r
3814                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3815                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3816                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3817                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3818                 }\r
3819             }\r
3820             else {\r
3821                 /* Dark square */\r
3822                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3823                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3824                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3825                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3826                 }\r
3827             }\r
3828         }\r
3829     }\r
3830 }\r
3831 \r
3832 /* [AS] Arrow highlighting support */\r
3833 \r
3834 static int A_WIDTH = 5; /* Width of arrow body */\r
3835 \r
3836 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3837 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3838 \r
3839 static double Sqr( double x )\r
3840 {\r
3841     return x*x;\r
3842 }\r
3843 \r
3844 static int Round( double x )\r
3845 {\r
3846     return (int) (x + 0.5);\r
3847 }\r
3848 \r
3849 /* Draw an arrow between two points using current settings */\r
3850 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3851 {\r
3852     POINT arrow[7];\r
3853     double dx, dy, j, k, x, y;\r
3854 \r
3855     if( d_x == s_x ) {\r
3856         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3857 \r
3858         arrow[0].x = s_x + A_WIDTH;\r
3859         arrow[0].y = s_y;\r
3860 \r
3861         arrow[1].x = s_x + A_WIDTH;\r
3862         arrow[1].y = d_y - h;\r
3863 \r
3864         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3865         arrow[2].y = d_y - h;\r
3866 \r
3867         arrow[3].x = d_x;\r
3868         arrow[3].y = d_y;\r
3869 \r
3870         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3871         arrow[4].y = d_y - h;\r
3872 \r
3873         arrow[5].x = s_x - A_WIDTH;\r
3874         arrow[5].y = d_y - h;\r
3875 \r
3876         arrow[6].x = s_x - A_WIDTH;\r
3877         arrow[6].y = s_y;\r
3878     }\r
3879     else if( d_y == s_y ) {\r
3880         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3881 \r
3882         arrow[0].x = s_x;\r
3883         arrow[0].y = s_y + A_WIDTH;\r
3884 \r
3885         arrow[1].x = d_x - w;\r
3886         arrow[1].y = s_y + A_WIDTH;\r
3887 \r
3888         arrow[2].x = d_x - w;\r
3889         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3890 \r
3891         arrow[3].x = d_x;\r
3892         arrow[3].y = d_y;\r
3893 \r
3894         arrow[4].x = d_x - w;\r
3895         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3896 \r
3897         arrow[5].x = d_x - w;\r
3898         arrow[5].y = s_y - A_WIDTH;\r
3899 \r
3900         arrow[6].x = s_x;\r
3901         arrow[6].y = s_y - A_WIDTH;\r
3902     }\r
3903     else {\r
3904         /* [AS] Needed a lot of paper for this! :-) */\r
3905         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3906         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3907   \r
3908         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3909 \r
3910         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3911 \r
3912         x = s_x;\r
3913         y = s_y;\r
3914 \r
3915         arrow[0].x = Round(x - j);\r
3916         arrow[0].y = Round(y + j*dx);\r
3917 \r
3918         arrow[1].x = Round(x + j);\r
3919         arrow[1].y = Round(y - j*dx);\r
3920 \r
3921         if( d_x > s_x ) {\r
3922             x = (double) d_x - k;\r
3923             y = (double) d_y - k*dy;\r
3924         }\r
3925         else {\r
3926             x = (double) d_x + k;\r
3927             y = (double) d_y + k*dy;\r
3928         }\r
3929 \r
3930         arrow[2].x = Round(x + j);\r
3931         arrow[2].y = Round(y - j*dx);\r
3932 \r
3933         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3934         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3935 \r
3936         arrow[4].x = d_x;\r
3937         arrow[4].y = d_y;\r
3938 \r
3939         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3940         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3941 \r
3942         arrow[6].x = Round(x - j);\r
3943         arrow[6].y = Round(y + j*dx);\r
3944     }\r
3945 \r
3946     Polygon( hdc, arrow, 7 );\r
3947 }\r
3948 \r
3949 /* [AS] Draw an arrow between two squares */\r
3950 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3951 {\r
3952     int s_x, s_y, d_x, d_y;\r
3953     HPEN hpen;\r
3954     HPEN holdpen;\r
3955     HBRUSH hbrush;\r
3956     HBRUSH holdbrush;\r
3957     LOGBRUSH stLB;\r
3958 \r
3959     if( s_col == d_col && s_row == d_row ) {\r
3960         return;\r
3961     }\r
3962 \r
3963     /* Get source and destination points */\r
3964     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3965     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3966 \r
3967     if( d_y > s_y ) {\r
3968         d_y += squareSize / 4;\r
3969     }\r
3970     else if( d_y < s_y ) {\r
3971         d_y += 3 * squareSize / 4;\r
3972     }\r
3973     else {\r
3974         d_y += squareSize / 2;\r
3975     }\r
3976 \r
3977     if( d_x > s_x ) {\r
3978         d_x += squareSize / 4;\r
3979     }\r
3980     else if( d_x < s_x ) {\r
3981         d_x += 3 * squareSize / 4;\r
3982     }\r
3983     else {\r
3984         d_x += squareSize / 2;\r
3985     }\r
3986 \r
3987     s_x += squareSize / 2;\r
3988     s_y += squareSize / 2;\r
3989 \r
3990     /* Adjust width */\r
3991     A_WIDTH = squareSize / 14;\r
3992 \r
3993     /* Draw */\r
3994     stLB.lbStyle = BS_SOLID;\r
3995     stLB.lbColor = appData.highlightArrowColor;\r
3996     stLB.lbHatch = 0;\r
3997 \r
3998     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3999     holdpen = SelectObject( hdc, hpen );\r
4000     hbrush = CreateBrushIndirect( &stLB );\r
4001     holdbrush = SelectObject( hdc, hbrush );\r
4002 \r
4003     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4004 \r
4005     SelectObject( hdc, holdpen );\r
4006     SelectObject( hdc, holdbrush );\r
4007     DeleteObject( hpen );\r
4008     DeleteObject( hbrush );\r
4009 }\r
4010 \r
4011 BOOL HasHighlightInfo()\r
4012 {\r
4013     BOOL result = FALSE;\r
4014 \r
4015     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4016         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4017     {\r
4018         result = TRUE;\r
4019     }\r
4020 \r
4021     return result;\r
4022 }\r
4023 \r
4024 BOOL IsDrawArrowEnabled()\r
4025 {\r
4026     BOOL result = FALSE;\r
4027 \r
4028     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4029         result = TRUE;\r
4030     }\r
4031 \r
4032     return result;\r
4033 }\r
4034 \r
4035 VOID DrawArrowHighlight( HDC hdc )\r
4036 {\r
4037     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4038         DrawArrowBetweenSquares( hdc,\r
4039             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4040             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4041     }\r
4042 }\r
4043 \r
4044 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4045 {\r
4046     HRGN result = NULL;\r
4047 \r
4048     if( HasHighlightInfo() ) {\r
4049         int x1, y1, x2, y2;\r
4050         int sx, sy, dx, dy;\r
4051 \r
4052         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4053         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4054 \r
4055         sx = MIN( x1, x2 );\r
4056         sy = MIN( y1, y2 );\r
4057         dx = MAX( x1, x2 ) + squareSize;\r
4058         dy = MAX( y1, y2 ) + squareSize;\r
4059 \r
4060         result = CreateRectRgn( sx, sy, dx, dy );\r
4061     }\r
4062 \r
4063     return result;\r
4064 }\r
4065 \r
4066 /*\r
4067     Warning: this function modifies the behavior of several other functions. \r
4068     \r
4069     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4070     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4071     repaint is scattered all over the place, which is not good for features such as\r
4072     "arrow highlighting" that require a full repaint of the board.\r
4073 \r
4074     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4075     user interaction, when speed is not so important) but especially to avoid errors\r
4076     in the displayed graphics.\r
4077 \r
4078     In such patched places, I always try refer to this function so there is a single\r
4079     place to maintain knowledge.\r
4080     \r
4081     To restore the original behavior, just return FALSE unconditionally.\r
4082 */\r
4083 BOOL IsFullRepaintPreferrable()\r
4084 {\r
4085     BOOL result = FALSE;\r
4086 \r
4087     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4088         /* Arrow may appear on the board */\r
4089         result = TRUE;\r
4090     }\r
4091 \r
4092     return result;\r
4093 }\r
4094 \r
4095 /* \r
4096     This function is called by DrawPosition to know whether a full repaint must\r
4097     be forced or not.\r
4098 \r
4099     Only DrawPosition may directly call this function, which makes use of \r
4100     some state information. Other function should call DrawPosition specifying \r
4101     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4102 */\r
4103 BOOL DrawPositionNeedsFullRepaint()\r
4104 {\r
4105     BOOL result = FALSE;\r
4106 \r
4107     /* \r
4108         Probably a slightly better policy would be to trigger a full repaint\r
4109         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4110         but animation is fast enough that it's difficult to notice.\r
4111     */\r
4112     if( animInfo.piece == EmptySquare ) {\r
4113         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4114             result = TRUE;\r
4115         }\r
4116     }\r
4117 \r
4118     return result;\r
4119 }\r
4120 \r
4121 VOID\r
4122 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4123 {\r
4124   int row, column, x, y, square_color, piece_color;\r
4125   ChessSquare piece;\r
4126   HBRUSH oldBrush;\r
4127   HDC texture_hdc = NULL;\r
4128 \r
4129   /* [AS] Initialize background textures if needed */\r
4130   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4131       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4132       if( backTextureSquareSize != squareSize \r
4133        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4134           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4135           backTextureSquareSize = squareSize;\r
4136           RebuildTextureSquareInfo();\r
4137       }\r
4138 \r
4139       texture_hdc = CreateCompatibleDC( hdc );\r
4140   }\r
4141 \r
4142   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4143     for (column = 0; column < BOARD_WIDTH; column++) {\r
4144   \r
4145       SquareToPos(row, column, &x, &y);\r
4146 \r
4147       piece = board[row][column];\r
4148 \r
4149       square_color = ((column + row) % 2) == 1;\r
4150       if( gameInfo.variant == VariantXiangqi ) {\r
4151           square_color = !InPalace(row, column);\r
4152           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4153           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4154       }\r
4155       piece_color = (int) piece < (int) BlackPawn;\r
4156 \r
4157 \r
4158       /* [HGM] holdings file: light square or black */\r
4159       if(column == BOARD_LEFT-2) {\r
4160             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4161                 square_color = 1;\r
4162             else {\r
4163                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4164                 continue;\r
4165             }\r
4166       } else\r
4167       if(column == BOARD_RGHT + 1 ) {\r
4168             if( row < gameInfo.holdingsSize )\r
4169                 square_color = 1;\r
4170             else {\r
4171                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4172                 continue;\r
4173             }\r
4174       }\r
4175       if(column == BOARD_LEFT-1 ) /* left align */\r
4176             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4177       else if( column == BOARD_RGHT) /* right align */\r
4178             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4179       else\r
4180       if (appData.monoMode) {\r
4181         if (piece == EmptySquare) {\r
4182           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4183                  square_color ? WHITENESS : BLACKNESS);\r
4184         } else {\r
4185           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4186         }\r
4187       } \r
4188       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4189           /* [AS] Draw the square using a texture bitmap */\r
4190           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4191           int r = row, c = column; // [HGM] do not flip board in flipView\r
4192           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4193 \r
4194           DrawTile( x, y, \r
4195               squareSize, squareSize, \r
4196               hdc, \r
4197               texture_hdc,\r
4198               backTextureSquareInfo[r][c].mode,\r
4199               backTextureSquareInfo[r][c].x,\r
4200               backTextureSquareInfo[r][c].y );\r
4201 \r
4202           SelectObject( texture_hdc, hbm );\r
4203 \r
4204           if (piece != EmptySquare) {\r
4205               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4206           }\r
4207       }\r
4208       else {\r
4209         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4210 \r
4211         oldBrush = SelectObject(hdc, brush );\r
4212         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4213         SelectObject(hdc, oldBrush);\r
4214         if (piece != EmptySquare)\r
4215           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4216       }\r
4217     }\r
4218   }\r
4219 \r
4220   if( texture_hdc != NULL ) {\r
4221     DeleteDC( texture_hdc );\r
4222   }\r
4223 }\r
4224 \r
4225 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4226 void fputDW(FILE *f, int x)\r
4227 {\r
4228         fputc(x     & 255, f);\r
4229         fputc(x>>8  & 255, f);\r
4230         fputc(x>>16 & 255, f);\r
4231         fputc(x>>24 & 255, f);\r
4232 }\r
4233 \r
4234 #define MAX_CLIPS 200   /* more than enough */\r
4235 \r
4236 VOID\r
4237 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4238 {\r
4239 //  HBITMAP bufferBitmap;\r
4240   BITMAP bi;\r
4241 //  RECT Rect;\r
4242   HDC tmphdc;\r
4243   HBITMAP hbm;\r
4244   int w = 100, h = 50;\r
4245 \r
4246   if(cps->programLogo == NULL) return;\r
4247 //  GetClientRect(hwndMain, &Rect);\r
4248 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4249 //                                      Rect.bottom-Rect.top+1);\r
4250   tmphdc = CreateCompatibleDC(hdc);\r
4251   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4252   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4253             w = bi.bmWidth;\r
4254             h = bi.bmHeight;\r
4255   }\r
4256   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4257                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4258   SelectObject(tmphdc, hbm);\r
4259   DeleteDC(tmphdc);\r
4260 }\r
4261 \r
4262 VOID\r
4263 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4264 {\r
4265   static Board lastReq, lastDrawn;\r
4266   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4267   static int lastDrawnFlipView = 0;\r
4268   static int lastReqValid = 0, lastDrawnValid = 0;\r
4269   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4270   HDC tmphdc;\r
4271   HDC hdcmem;\r
4272   HBITMAP bufferBitmap;\r
4273   HBITMAP oldBitmap;\r
4274   RECT Rect;\r
4275   HRGN clips[MAX_CLIPS];\r
4276   ChessSquare dragged_piece = EmptySquare;\r
4277 \r
4278   /* I'm undecided on this - this function figures out whether a full\r
4279    * repaint is necessary on its own, so there's no real reason to have the\r
4280    * caller tell it that.  I think this can safely be set to FALSE - but\r
4281    * if we trust the callers not to request full repaints unnessesarily, then\r
4282    * we could skip some clipping work.  In other words, only request a full\r
4283    * redraw when the majority of pieces have changed positions (ie. flip, \r
4284    * gamestart and similar)  --Hawk\r
4285    */\r
4286   Boolean fullrepaint = repaint;\r
4287 \r
4288   if( DrawPositionNeedsFullRepaint() ) {\r
4289       fullrepaint = TRUE;\r
4290   }\r
4291 \r
4292 #if 0\r
4293   if( fullrepaint ) {\r
4294       static int repaint_count = 0;\r
4295       char buf[128];\r
4296 \r
4297       repaint_count++;\r
4298       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4299       OutputDebugString( buf );\r
4300   }\r
4301 #endif\r
4302 \r
4303   if (board == NULL) {\r
4304     if (!lastReqValid) {\r
4305       return;\r
4306     }\r
4307     board = lastReq;\r
4308   } else {\r
4309     CopyBoard(lastReq, board);\r
4310     lastReqValid = 1;\r
4311   }\r
4312 \r
4313   if (doingSizing) {\r
4314     return;\r
4315   }\r
4316 \r
4317   if (IsIconic(hwndMain)) {\r
4318     return;\r
4319   }\r
4320 \r
4321   if (hdc == NULL) {\r
4322     hdc = GetDC(hwndMain);\r
4323     if (!appData.monoMode) {\r
4324       SelectPalette(hdc, hPal, FALSE);\r
4325       RealizePalette(hdc);\r
4326     }\r
4327     releaseDC = TRUE;\r
4328   } else {\r
4329     releaseDC = FALSE;\r
4330   }\r
4331 \r
4332 #if 0\r
4333   fprintf(debugFP, "*******************************\n"\r
4334                    "repaint = %s\n"\r
4335                    "dragInfo.from (%d,%d)\n"\r
4336                    "dragInfo.start (%d,%d)\n"\r
4337                    "dragInfo.pos (%d,%d)\n"\r
4338                    "dragInfo.lastpos (%d,%d)\n", \r
4339                     repaint ? "TRUE" : "FALSE",\r
4340                     dragInfo.from.x, dragInfo.from.y, \r
4341                     dragInfo.start.x, dragInfo.start.y,\r
4342                     dragInfo.pos.x, dragInfo.pos.y,\r
4343                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4344   fprintf(debugFP, "prev:  ");\r
4345   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4346     for (column = 0; column < BOARD_WIDTH; column++) {\r
4347       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4348     }\r
4349   }\r
4350   fprintf(debugFP, "\n");\r
4351   fprintf(debugFP, "board: ");\r
4352   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4353     for (column = 0; column < BOARD_WIDTH; column++) {\r
4354       fprintf(debugFP, "%d ", board[row][column]);\r
4355     }\r
4356   }\r
4357   fprintf(debugFP, "\n");\r
4358   fflush(debugFP);\r
4359 #endif\r
4360 \r
4361   /* Create some work-DCs */\r
4362   hdcmem = CreateCompatibleDC(hdc);\r
4363   tmphdc = CreateCompatibleDC(hdc);\r
4364 \r
4365   /* If dragging is in progress, we temporarely remove the piece */\r
4366   /* [HGM] or temporarily decrease count if stacked              */\r
4367   /*       !! Moved to before board compare !!                   */\r
4368   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4369     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4370     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4371             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4372         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4373     } else \r
4374     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4375             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4376         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4377     } else \r
4378         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4379   }\r
4380 \r
4381   /* Figure out which squares need updating by comparing the \r
4382    * newest board with the last drawn board and checking if\r
4383    * flipping has changed.\r
4384    */\r
4385   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4386     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4387       for (column = 0; column < BOARD_WIDTH; column++) {\r
4388         if (lastDrawn[row][column] != board[row][column]) {\r
4389           SquareToPos(row, column, &x, &y);\r
4390           clips[num_clips++] =\r
4391             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4392         }\r
4393       }\r
4394     }\r
4395     for (i=0; i<2; i++) {\r
4396       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4397           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4398         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4399             lastDrawnHighlight.sq[i].y >= 0) {\r
4400           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4401                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4402           clips[num_clips++] =\r
4403             CreateRectRgn(x - lineGap, y - lineGap, \r
4404                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4405         }\r
4406         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4407           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4408           clips[num_clips++] =\r
4409             CreateRectRgn(x - lineGap, y - lineGap, \r
4410                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4411         }\r
4412       }\r
4413     }\r
4414     for (i=0; i<2; i++) {\r
4415       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4416           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4417         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4418             lastDrawnPremove.sq[i].y >= 0) {\r
4419           SquareToPos(lastDrawnPremove.sq[i].y,\r
4420                       lastDrawnPremove.sq[i].x, &x, &y);\r
4421           clips[num_clips++] =\r
4422             CreateRectRgn(x - lineGap, y - lineGap, \r
4423                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4424         }\r
4425         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4426             premoveHighlightInfo.sq[i].y >= 0) {\r
4427           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4428                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4429           clips[num_clips++] =\r
4430             CreateRectRgn(x - lineGap, y - lineGap, \r
4431                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4432         }\r
4433       }\r
4434     }\r
4435   } else {\r
4436     fullrepaint = TRUE;\r
4437   }\r
4438 \r
4439   /* Create a buffer bitmap - this is the actual bitmap\r
4440    * being written to.  When all the work is done, we can\r
4441    * copy it to the real DC (the screen).  This avoids\r
4442    * the problems with flickering.\r
4443    */\r
4444   GetClientRect(hwndMain, &Rect);\r
4445   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4446                                         Rect.bottom-Rect.top+1);\r
4447   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4448   if (!appData.monoMode) {\r
4449     SelectPalette(hdcmem, hPal, FALSE);\r
4450   }\r
4451 \r
4452   /* Create clips for dragging */\r
4453   if (!fullrepaint) {\r
4454     if (dragInfo.from.x >= 0) {\r
4455       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4456       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4457     }\r
4458     if (dragInfo.start.x >= 0) {\r
4459       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4460       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4461     }\r
4462     if (dragInfo.pos.x >= 0) {\r
4463       x = dragInfo.pos.x - squareSize / 2;\r
4464       y = dragInfo.pos.y - squareSize / 2;\r
4465       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4466     }\r
4467     if (dragInfo.lastpos.x >= 0) {\r
4468       x = dragInfo.lastpos.x - squareSize / 2;\r
4469       y = dragInfo.lastpos.y - squareSize / 2;\r
4470       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4471     }\r
4472   }\r
4473 \r
4474   /* Are we animating a move?  \r
4475    * If so, \r
4476    *   - remove the piece from the board (temporarely)\r
4477    *   - calculate the clipping region\r
4478    */\r
4479   if (!fullrepaint) {\r
4480     if (animInfo.piece != EmptySquare) {\r
4481       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4482       x = boardRect.left + animInfo.lastpos.x;\r
4483       y = boardRect.top + animInfo.lastpos.y;\r
4484       x2 = boardRect.left + animInfo.pos.x;\r
4485       y2 = boardRect.top + animInfo.pos.y;\r
4486       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4487       /* Slight kludge.  The real problem is that after AnimateMove is\r
4488          done, the position on the screen does not match lastDrawn.\r
4489          This currently causes trouble only on e.p. captures in\r
4490          atomic, where the piece moves to an empty square and then\r
4491          explodes.  The old and new positions both had an empty square\r
4492          at the destination, but animation has drawn a piece there and\r
4493          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4494       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4495     }\r
4496   }\r
4497 \r
4498   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4499   if (num_clips == 0)\r
4500     fullrepaint = TRUE;\r
4501 \r
4502   /* Set clipping on the memory DC */\r
4503   if (!fullrepaint) {\r
4504     SelectClipRgn(hdcmem, clips[0]);\r
4505     for (x = 1; x < num_clips; x++) {\r
4506       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4507         abort();  // this should never ever happen!\r
4508     }\r
4509   }\r
4510 \r
4511   /* Do all the drawing to the memory DC */\r
4512   if(explodeInfo.radius) { // [HGM] atomic\r
4513         HBRUSH oldBrush;\r
4514         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4515         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4516         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4517         x += squareSize/2;\r
4518         y += squareSize/2;\r
4519         if(!fullrepaint) {\r
4520           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4521           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4522         }\r
4523         DrawGridOnDC(hdcmem);\r
4524         DrawHighlightsOnDC(hdcmem);\r
4525         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4526         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4527         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4528         SelectObject(hdcmem, oldBrush);\r
4529   } else {\r
4530     DrawGridOnDC(hdcmem);\r
4531     DrawHighlightsOnDC(hdcmem);\r
4532     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4533   }\r
4534   if(logoHeight) {\r
4535         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4536         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4537   }\r
4538 \r
4539   if( appData.highlightMoveWithArrow ) {\r
4540     DrawArrowHighlight(hdcmem);\r
4541   }\r
4542 \r
4543   DrawCoordsOnDC(hdcmem);\r
4544 \r
4545   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4546                  /* to make sure lastDrawn contains what is actually drawn */\r
4547 \r
4548   /* Put the dragged piece back into place and draw it (out of place!) */\r
4549     if (dragged_piece != EmptySquare) {\r
4550     /* [HGM] or restack */\r
4551     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4552                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4553     else\r
4554     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4555                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4556     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4557     x = dragInfo.pos.x - squareSize / 2;\r
4558     y = dragInfo.pos.y - squareSize / 2;\r
4559     DrawPieceOnDC(hdcmem, dragged_piece,\r
4560                   ((int) dragged_piece < (int) BlackPawn), \r
4561                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4562   }   \r
4563   \r
4564   /* Put the animated piece back into place and draw it */\r
4565   if (animInfo.piece != EmptySquare) {\r
4566     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4567     x = boardRect.left + animInfo.pos.x;\r
4568     y = boardRect.top + animInfo.pos.y;\r
4569     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4570                   ((int) animInfo.piece < (int) BlackPawn),\r
4571                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4572   }\r
4573 \r
4574   /* Release the bufferBitmap by selecting in the old bitmap \r
4575    * and delete the memory DC\r
4576    */\r
4577   SelectObject(hdcmem, oldBitmap);\r
4578   DeleteDC(hdcmem);\r
4579 \r
4580   /* Set clipping on the target DC */\r
4581   if (!fullrepaint) {\r
4582     SelectClipRgn(hdc, clips[0]);\r
4583     for (x = 1; x < num_clips; x++) {\r
4584       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4585         abort();   // this should never ever happen!\r
4586     } \r
4587   }\r
4588 \r
4589   /* Copy the new bitmap onto the screen in one go.\r
4590    * This way we avoid any flickering\r
4591    */\r
4592   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4593   BitBlt(hdc, boardRect.left, boardRect.top,\r
4594          boardRect.right - boardRect.left,\r
4595          boardRect.bottom - boardRect.top,\r
4596          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4597   if(saveDiagFlag) { \r
4598     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4599     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4600 \r
4601     GetObject(bufferBitmap, sizeof(b), &b);\r
4602     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4603         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4604         bih.biWidth = b.bmWidth;\r
4605         bih.biHeight = b.bmHeight;\r
4606         bih.biPlanes = 1;\r
4607         bih.biBitCount = b.bmBitsPixel;\r
4608         bih.biCompression = 0;\r
4609         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4610         bih.biXPelsPerMeter = 0;\r
4611         bih.biYPelsPerMeter = 0;\r
4612         bih.biClrUsed = 0;\r
4613         bih.biClrImportant = 0;\r
4614 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4615 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4616         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4617 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4618 \r
4619 #if 1\r
4620         wb = b.bmWidthBytes;\r
4621         // count colors\r
4622         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4623                 int k = ((int*) pData)[i];\r
4624                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4625                 if(j >= 16) break;\r
4626                 color[j] = k;\r
4627                 if(j >= nrColors) nrColors = j+1;\r
4628         }\r
4629         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4630                 INT p = 0;\r
4631                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4632                     for(w=0; w<(wb>>2); w+=2) {\r
4633                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4634                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4635                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4636                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4637                         pData[p++] = m | j<<4;\r
4638                     }\r
4639                     while(p&3) pData[p++] = 0;\r
4640                 }\r
4641                 fac = 3;\r
4642                 wb = ((wb+31)>>5)<<2;\r
4643         }\r
4644         // write BITMAPFILEHEADER\r
4645         fprintf(diagFile, "BM");\r
4646         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4647         fputDW(diagFile, 0);\r
4648         fputDW(diagFile, 0x36 + (fac?64:0));\r
4649         // write BITMAPINFOHEADER\r
4650         fputDW(diagFile, 40);\r
4651         fputDW(diagFile, b.bmWidth);\r
4652         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4653         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4654         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         fputDW(diagFile, 0);\r
4658         fputDW(diagFile, 0);\r
4659         fputDW(diagFile, 0);\r
4660         fputDW(diagFile, 0);\r
4661         // write color table\r
4662         if(fac)\r
4663         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4664         // write bitmap data\r
4665         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4666                 fputc(pData[i], diagFile);\r
4667 #endif\r
4668      }\r
4669   }\r
4670 \r
4671   SelectObject(tmphdc, oldBitmap);\r
4672 \r
4673   /* Massive cleanup */\r
4674   for (x = 0; x < num_clips; x++)\r
4675     DeleteObject(clips[x]);\r
4676 \r
4677   DeleteDC(tmphdc);\r
4678   DeleteObject(bufferBitmap);\r
4679 \r
4680   if (releaseDC) \r
4681     ReleaseDC(hwndMain, hdc);\r
4682   \r
4683   if (lastDrawnFlipView != flipView) {\r
4684     if (flipView)\r
4685       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4686     else\r
4687       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4688   }\r
4689 \r
4690 /*  CopyBoard(lastDrawn, board);*/\r
4691   lastDrawnHighlight = highlightInfo;\r
4692   lastDrawnPremove   = premoveHighlightInfo;\r
4693   lastDrawnFlipView = flipView;\r
4694   lastDrawnValid = 1;\r
4695 }\r
4696 \r
4697 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4698 int\r
4699 SaveDiagram(f)\r
4700      FILE *f;\r
4701 {\r
4702     saveDiagFlag = 1; diagFile = f;\r
4703     HDCDrawPosition(NULL, TRUE, NULL);\r
4704 \r
4705     saveDiagFlag = 0;\r
4706 \r
4707 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4708     \r
4709     fclose(f);\r
4710     return TRUE;\r
4711 }\r
4712 \r
4713 \r
4714 /*---------------------------------------------------------------------------*\\r
4715 | CLIENT PAINT PROCEDURE\r
4716 |   This is the main event-handler for the WM_PAINT message.\r
4717 |\r
4718 \*---------------------------------------------------------------------------*/\r
4719 VOID\r
4720 PaintProc(HWND hwnd)\r
4721 {\r
4722   HDC         hdc;\r
4723   PAINTSTRUCT ps;\r
4724   HFONT       oldFont;\r
4725 \r
4726   if((hdc = BeginPaint(hwnd, &ps))) {\r
4727     if (IsIconic(hwnd)) {\r
4728       DrawIcon(hdc, 2, 2, iconCurrent);\r
4729     } else {\r
4730       if (!appData.monoMode) {\r
4731         SelectPalette(hdc, hPal, FALSE);\r
4732         RealizePalette(hdc);\r
4733       }\r
4734       HDCDrawPosition(hdc, 1, NULL);\r
4735       oldFont =\r
4736         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4737       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4738                  ETO_CLIPPED|ETO_OPAQUE,\r
4739                  &messageRect, messageText, strlen(messageText), NULL);\r
4740       SelectObject(hdc, oldFont);\r
4741       DisplayBothClocks();\r
4742     }\r
4743     EndPaint(hwnd,&ps);\r
4744   }\r
4745 \r
4746   return;\r
4747 }\r
4748 \r
4749 \r
4750 /*\r
4751  * If the user selects on a border boundary, return -1; if off the board,\r
4752  *   return -2.  Otherwise map the event coordinate to the square.\r
4753  * The offset boardRect.left or boardRect.top must already have been\r
4754  *   subtracted from x.\r
4755  */\r
4756 int\r
4757 EventToSquare(int x)\r
4758 {\r
4759   if (x <= 0)\r
4760     return -2;\r
4761   if (x < lineGap)\r
4762     return -1;\r
4763   x -= lineGap;\r
4764   if ((x % (squareSize + lineGap)) >= squareSize)\r
4765     return -1;\r
4766   x /= (squareSize + lineGap);\r
4767   if (x >= BOARD_SIZE)\r
4768     return -2;\r
4769   return x;\r
4770 }\r
4771 \r
4772 typedef struct {\r
4773   char piece;\r
4774   int command;\r
4775   char* name;\r
4776 } DropEnable;\r
4777 \r
4778 DropEnable dropEnables[] = {\r
4779   { 'P', DP_Pawn, "Pawn" },\r
4780   { 'N', DP_Knight, "Knight" },\r
4781   { 'B', DP_Bishop, "Bishop" },\r
4782   { 'R', DP_Rook, "Rook" },\r
4783   { 'Q', DP_Queen, "Queen" },\r
4784 };\r
4785 \r
4786 VOID\r
4787 SetupDropMenu(HMENU hmenu)\r
4788 {\r
4789   int i, count, enable;\r
4790   char *p;\r
4791   extern char white_holding[], black_holding[];\r
4792   char item[MSG_SIZ];\r
4793 \r
4794   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4795     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4796                dropEnables[i].piece);\r
4797     count = 0;\r
4798     while (p && *p++ == dropEnables[i].piece) count++;\r
4799     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4800     enable = count > 0 || !appData.testLegality\r
4801       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4802                       && !appData.icsActive);\r
4803     ModifyMenu(hmenu, dropEnables[i].command,\r
4804                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4805                dropEnables[i].command, item);\r
4806   }\r
4807 }\r
4808 \r
4809 static int fromX = -1, fromY = -1, toX, toY;\r
4810 \r
4811 /* Event handler for mouse messages */\r
4812 VOID\r
4813 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4814 {\r
4815   int x, y;\r
4816   POINT pt;\r
4817   static int recursive = 0;\r
4818   HMENU hmenu;\r
4819 //  BOOLEAN needsRedraw = FALSE;\r
4820   BOOLEAN saveAnimate;\r
4821   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4822   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4823   ChessMove moveType;\r
4824 \r
4825   if (recursive) {\r
4826     if (message == WM_MBUTTONUP) {\r
4827       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4828          to the middle button: we simulate pressing the left button too!\r
4829          */\r
4830       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4831       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4832     }\r
4833     return;\r
4834   }\r
4835   recursive++;\r
4836   \r
4837   pt.x = LOWORD(lParam);\r
4838   pt.y = HIWORD(lParam);\r
4839   x = EventToSquare(pt.x - boardRect.left);\r
4840   y = EventToSquare(pt.y - boardRect.top);\r
4841   if (!flipView && y >= 0) {\r
4842     y = BOARD_HEIGHT - 1 - y;\r
4843   }\r
4844   if (flipView && x >= 0) {\r
4845     x = BOARD_WIDTH - 1 - x;\r
4846   }\r
4847 \r
4848   switch (message) {\r
4849   case WM_LBUTTONDOWN:\r
4850     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4851         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4852         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4853         if(gameInfo.holdingsWidth && \r
4854                 (WhiteOnMove(currentMove) \r
4855                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4856                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4857             // click in right holdings, for determining promotion piece\r
4858             ChessSquare p = boards[currentMove][y][x];\r
4859             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4860             if(p != EmptySquare) {\r
4861                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4862                 fromX = fromY = -1;\r
4863                 break;\r
4864             }\r
4865         }\r
4866         DrawPosition(FALSE, boards[currentMove]);\r
4867         break;\r
4868     }\r
4869     ErrorPopDown();\r
4870     sameAgain = FALSE;\r
4871     if (y == -2) {\r
4872       /* Downclick vertically off board; check if on clock */\r
4873       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4874         if (gameMode == EditPosition) {\r
4875           SetWhiteToPlayEvent();\r
4876         } else if (gameMode == IcsPlayingBlack ||\r
4877                    gameMode == MachinePlaysWhite) {\r
4878           CallFlagEvent();\r
4879         } else if (gameMode == EditGame) {\r
4880           AdjustClock(flipClock, -1);\r
4881         }\r
4882       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4883         if (gameMode == EditPosition) {\r
4884           SetBlackToPlayEvent();\r
4885         } else if (gameMode == IcsPlayingWhite ||\r
4886                    gameMode == MachinePlaysBlack) {\r
4887           CallFlagEvent();\r
4888         } else if (gameMode == EditGame) {\r
4889           AdjustClock(!flipClock, -1);\r
4890         }\r
4891       }\r
4892       if (!appData.highlightLastMove) {\r
4893         ClearHighlights();\r
4894         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4895       }\r
4896       fromX = fromY = -1;\r
4897       dragInfo.start.x = dragInfo.start.y = -1;\r
4898       dragInfo.from = dragInfo.start;\r
4899       break;\r
4900     } else if (x < 0 || y < 0\r
4901       /* [HGM] block clicks between board and holdings */\r
4902               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4903               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4904               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4905         /* EditPosition, empty square, or different color piece;\r
4906            click-click move is possible */\r
4907                                ) {\r
4908       break;\r
4909     } else if (fromX == x && fromY == y) {\r
4910       /* Downclick on same square again */\r
4911       ClearHighlights();\r
4912       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4913       sameAgain = TRUE;  \r
4914     } else if (fromX != -1 &&\r
4915                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4916                                                                         ) {\r
4917       /* Downclick on different square. */\r
4918       /* [HGM] if on holdings file, should count as new first click ! */\r
4919       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4920         toX = x;\r
4921         toY = y;\r
4922         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4923            to make sure move is legal before showing promotion popup */\r
4924         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4925         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4926                 fromX = fromY = -1; \r
4927                 ClearHighlights();\r
4928                 DrawPosition(FALSE, boards[currentMove]);\r
4929                 break; \r
4930         } else \r
4931         if(moveType != ImpossibleMove) {\r
4932           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4933           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4934             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4935               appData.alwaysPromoteToQueen)) {\r
4936                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4937                   if (!appData.highlightLastMove) {\r
4938                       ClearHighlights();\r
4939                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4940                   }\r
4941           } else\r
4942           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4943                   SetHighlights(fromX, fromY, toX, toY);\r
4944                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4945                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4946                      If promotion to Q is legal, all are legal! */\r
4947                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4948                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4949                     // kludge to temporarily execute move on display, wthout promotng yet\r
4950                     promotionChoice = TRUE;\r
4951                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4952                     boards[currentMove][toY][toX] = p;\r
4953                     DrawPosition(FALSE, boards[currentMove]);\r
4954                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4955                     boards[currentMove][toY][toX] = q;\r
4956                   } else\r
4957                   PromotionPopup(hwnd);\r
4958           } else {       /* not a promotion */\r
4959              if (appData.animate || appData.highlightLastMove) {\r
4960                  SetHighlights(fromX, fromY, toX, toY);\r
4961              } else {\r
4962                  ClearHighlights();\r
4963              }\r
4964              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4965              fromX = fromY = -1;\r
4966              if (appData.animate && !appData.highlightLastMove) {\r
4967                   ClearHighlights();\r
4968                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4969              }\r
4970           }\r
4971           break;\r
4972         }\r
4973         if (gotPremove) {\r
4974             /* [HGM] it seemed that braces were missing here */\r
4975             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4976             fromX = fromY = -1;\r
4977             break;\r
4978         }\r
4979       }\r
4980       ClearHighlights();\r
4981       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4982     }\r
4983     /* First downclick, or restart on a square with same color piece */\r
4984     if (!frozen && OKToStartUserMove(x, y)) {\r
4985       fromX = x;\r
4986       fromY = y;\r
4987       dragInfo.lastpos = pt;\r
4988       dragInfo.from.x = fromX;\r
4989       dragInfo.from.y = fromY;\r
4990       dragInfo.start = dragInfo.from;\r
4991       SetCapture(hwndMain);\r
4992     } else {\r
4993       fromX = fromY = -1;\r
4994       dragInfo.start.x = dragInfo.start.y = -1;\r
4995       dragInfo.from = dragInfo.start;\r
4996       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4997     }\r
4998     break;\r
4999 \r
5000   case WM_LBUTTONUP:\r
5001     ReleaseCapture();\r
5002     if (fromX == -1) break;\r
5003     if (x == fromX && y == fromY) {\r
5004       dragInfo.from.x = dragInfo.from.y = -1;\r
5005       /* Upclick on same square */\r
5006       if (sameAgain) {\r
5007         /* Clicked same square twice: abort click-click move */\r
5008         fromX = fromY = -1;\r
5009         gotPremove = 0;\r
5010         ClearPremoveHighlights();\r
5011       } else {\r
5012         /* First square clicked: start click-click move */\r
5013         SetHighlights(fromX, fromY, -1, -1);\r
5014       }\r
5015       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5016     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5017       /* Errant click; ignore */\r
5018       break;\r
5019     } else {\r
5020       /* Finish drag move. */\r
5021     if (appData.debugMode) {\r
5022         fprintf(debugFP, "release\n");\r
5023     }\r
5024       dragInfo.from.x = dragInfo.from.y = -1;\r
5025       toX = x;\r
5026       toY = y;\r
5027       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5028       appData.animate = appData.animate && !appData.animateDragging;\r
5029       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5030       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5031                 fromX = fromY = -1; \r
5032                 ClearHighlights();\r
5033                 DrawPosition(FALSE, boards[currentMove]);\r
5034                 break; \r
5035       } else \r
5036       if(moveType != ImpossibleMove) {\r
5037           /* [HGM] use move type to determine if move is promotion.\r
5038              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5039           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5040             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5041               appData.alwaysPromoteToQueen)) \r
5042                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5043           else \r
5044           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5045                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5046                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5047                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5048                     // kludge to temporarily execute move on display, wthout promotng yet\r
5049                     promotionChoice = TRUE;\r
5050                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5051                     boards[currentMove][toY][toX] = p;\r
5052                     DrawPosition(FALSE, boards[currentMove]);\r
5053                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5054                     boards[currentMove][toY][toX] = q;\r
5055                     break;\r
5056                   } else\r
5057                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5058           } else {\r
5059             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5060                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5061                                         moveType == WhiteCapturesEnPassant || \r
5062                                         moveType == BlackCapturesEnPassant   ) )\r
5063                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5064             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5065           }\r
5066       }\r
5067       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5068       appData.animate = saveAnimate;\r
5069       fromX = fromY = -1;\r
5070       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5071         ClearHighlights();\r
5072       }\r
5073       if (appData.animate || appData.animateDragging ||\r
5074           appData.highlightDragging || gotPremove) {\r
5075         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5076       }\r
5077     }\r
5078     dragInfo.start.x = dragInfo.start.y = -1; \r
5079     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5080     break;\r
5081 \r
5082   case WM_MOUSEMOVE:\r
5083     if ((appData.animateDragging || appData.highlightDragging)\r
5084         && (wParam & MK_LBUTTON)\r
5085         && dragInfo.from.x >= 0) \r
5086     {\r
5087       BOOL full_repaint = FALSE;\r
5088 \r
5089       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5090       if (appData.animateDragging) {\r
5091         dragInfo.pos = pt;\r
5092       }\r
5093       if (appData.highlightDragging) {\r
5094         SetHighlights(fromX, fromY, x, y);\r
5095         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5096             full_repaint = TRUE;\r
5097         }\r
5098       }\r
5099       \r
5100       DrawPosition( full_repaint, NULL);\r
5101       \r
5102       dragInfo.lastpos = dragInfo.pos;\r
5103     }\r
5104     break;\r
5105 \r
5106   case WM_MOUSEWHEEL: // [DM]\r
5107     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5108        /* Mouse Wheel is being rolled forward\r
5109         * Play moves forward\r
5110         */\r
5111        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5112                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5113        /* Mouse Wheel is being rolled backward\r
5114         * Play moves backward\r
5115         */\r
5116        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5117                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5118     }\r
5119     break;\r
5120 \r
5121   case WM_MBUTTONDOWN:\r
5122   case WM_RBUTTONDOWN:\r
5123     ErrorPopDown();\r
5124     ReleaseCapture();\r
5125     fromX = fromY = -1;\r
5126     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5127     dragInfo.start.x = dragInfo.start.y = -1;\r
5128     dragInfo.from = dragInfo.start;\r
5129     dragInfo.lastpos = dragInfo.pos;\r
5130     if (appData.highlightDragging) {\r
5131       ClearHighlights();\r
5132     }\r
5133     if(y == -2) {\r
5134       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5135       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5136           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5137       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5138           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5139       }\r
5140     }\r
5141     DrawPosition(TRUE, NULL);\r
5142 \r
5143     switch (gameMode) {\r
5144     case EditPosition:\r
5145     case IcsExamining:\r
5146       if (x < 0 || y < 0) break;\r
5147       fromX = x;\r
5148       fromY = y;\r
5149       if (message == WM_MBUTTONDOWN) {\r
5150         buttonCount = 3;  /* even if system didn't think so */\r
5151         if (wParam & MK_SHIFT) \r
5152           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5153         else\r
5154           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5155       } else { /* message == WM_RBUTTONDOWN */\r
5156 #if 0\r
5157         if (buttonCount == 3) {\r
5158           if (wParam & MK_SHIFT) \r
5159             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5160           else\r
5161             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5162         } else {\r
5163           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5164         }\r
5165 #else\r
5166         /* Just have one menu, on the right button.  Windows users don't\r
5167            think to try the middle one, and sometimes other software steals\r
5168            it, or it doesn't really exist. */\r
5169         if(gameInfo.variant != VariantShogi)\r
5170             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5171         else\r
5172             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5173 #endif\r
5174       }\r
5175       break;\r
5176     case IcsPlayingWhite:\r
5177     case IcsPlayingBlack:\r
5178     case EditGame:\r
5179     case MachinePlaysWhite:\r
5180     case MachinePlaysBlack:\r
5181       if (appData.testLegality &&\r
5182           gameInfo.variant != VariantBughouse &&\r
5183           gameInfo.variant != VariantCrazyhouse) break;\r
5184       if (x < 0 || y < 0) break;\r
5185       fromX = x;\r
5186       fromY = y;\r
5187       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5188       SetupDropMenu(hmenu);\r
5189       MenuPopup(hwnd, pt, hmenu, -1);\r
5190       break;\r
5191     default:\r
5192       break;\r
5193     }\r
5194     break;\r
5195   }\r
5196 \r
5197   recursive--;\r
5198 }\r
5199 \r
5200 /* Preprocess messages for buttons in main window */\r
5201 LRESULT CALLBACK\r
5202 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5203 {\r
5204   int id = GetWindowLong(hwnd, GWL_ID);\r
5205   int i, dir;\r
5206 \r
5207   for (i=0; i<N_BUTTONS; i++) {\r
5208     if (buttonDesc[i].id == id) break;\r
5209   }\r
5210   if (i == N_BUTTONS) return 0;\r
5211   switch (message) {\r
5212   case WM_KEYDOWN:\r
5213     switch (wParam) {\r
5214     case VK_LEFT:\r
5215     case VK_RIGHT:\r
5216       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5217       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5218       return TRUE;\r
5219     }\r
5220     break;\r
5221   case WM_CHAR:\r
5222     switch (wParam) {\r
5223     case '\r':\r
5224       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5225       return TRUE;\r
5226     case '\t':\r
5227       if (appData.icsActive) {\r
5228         if (GetKeyState(VK_SHIFT) < 0) {\r
5229           /* shifted */\r
5230           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5231           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5232           SetFocus(h);\r
5233         } else {\r
5234           /* unshifted */\r
5235           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5236           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5237           SetFocus(h);\r
5238         }\r
5239         return TRUE;\r
5240       }\r
5241       break;\r
5242     default:\r
5243       if (appData.icsActive) {\r
5244         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5245         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5246         SetFocus(h);\r
5247         SendMessage(h, WM_CHAR, wParam, lParam);\r
5248         return TRUE;\r
5249       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5250         PopUpMoveDialog((char)wParam);\r
5251       }\r
5252       break;\r
5253     }\r
5254     break;\r
5255   }\r
5256   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5257 }\r
5258 \r
5259 /* Process messages for Promotion dialog box */\r
5260 LRESULT CALLBACK\r
5261 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5262 {\r
5263   char promoChar;\r
5264 \r
5265   switch (message) {\r
5266   case WM_INITDIALOG: /* message: initialize dialog box */\r
5267     /* Center the dialog over the application window */\r
5268     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5269     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5270       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5271        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5272                SW_SHOW : SW_HIDE);\r
5273     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5274     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5275        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5276          PieceToChar(WhiteAngel) != '~') ||\r
5277         (PieceToChar(BlackAngel) >= 'A' &&\r
5278          PieceToChar(BlackAngel) != '~')   ) ?\r
5279                SW_SHOW : SW_HIDE);\r
5280     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5281        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5282          PieceToChar(WhiteMarshall) != '~') ||\r
5283         (PieceToChar(BlackMarshall) >= 'A' &&\r
5284          PieceToChar(BlackMarshall) != '~')   ) ?\r
5285                SW_SHOW : SW_HIDE);\r
5286     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5287     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5288        gameInfo.variant != VariantShogi ?\r
5289                SW_SHOW : SW_HIDE);\r
5290     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5291        gameInfo.variant != VariantShogi ?\r
5292                SW_SHOW : SW_HIDE);\r
5293     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5294        gameInfo.variant == VariantShogi ?\r
5295                SW_SHOW : SW_HIDE);\r
5296     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5297        gameInfo.variant == VariantShogi ?\r
5298                SW_SHOW : SW_HIDE);\r
5299     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5300        gameInfo.variant == VariantSuper ?\r
5301                SW_SHOW : SW_HIDE);\r
5302     return TRUE;\r
5303 \r
5304   case WM_COMMAND: /* message: received a command */\r
5305     switch (LOWORD(wParam)) {\r
5306     case IDCANCEL:\r
5307       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5308       ClearHighlights();\r
5309       DrawPosition(FALSE, NULL);\r
5310       return TRUE;\r
5311     case PB_King:\r
5312       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5313       break;\r
5314     case PB_Queen:\r
5315       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5316       break;\r
5317     case PB_Rook:\r
5318       promoChar = PieceToChar(BlackRook);\r
5319       break;\r
5320     case PB_Bishop:\r
5321       promoChar = PieceToChar(BlackBishop);\r
5322       break;\r
5323     case PB_Chancellor:\r
5324       promoChar = PieceToChar(BlackMarshall);\r
5325       break;\r
5326     case PB_Archbishop:\r
5327       promoChar = PieceToChar(BlackAngel);\r
5328       break;\r
5329     case PB_Knight:\r
5330       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5331       break;\r
5332     default:\r
5333       return FALSE;\r
5334     }\r
5335     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5336     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5337        only show the popup when we are already sure the move is valid or\r
5338        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5339        will figure out it is a promotion from the promoChar. */\r
5340     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5341     if (!appData.highlightLastMove) {\r
5342       ClearHighlights();\r
5343       DrawPosition(FALSE, NULL);\r
5344     }\r
5345     return TRUE;\r
5346   }\r
5347   return FALSE;\r
5348 }\r
5349 \r
5350 /* Pop up promotion dialog */\r
5351 VOID\r
5352 PromotionPopup(HWND hwnd)\r
5353 {\r
5354   FARPROC lpProc;\r
5355 \r
5356   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5357   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5358     hwnd, (DLGPROC)lpProc);\r
5359   FreeProcInstance(lpProc);\r
5360 }\r
5361 \r
5362 /* Toggle ShowThinking */\r
5363 VOID\r
5364 ToggleShowThinking()\r
5365 {\r
5366   appData.showThinking = !appData.showThinking;\r
5367   ShowThinkingEvent();\r
5368 }\r
5369 \r
5370 VOID\r
5371 LoadGameDialog(HWND hwnd, char* title)\r
5372 {\r
5373   UINT number = 0;\r
5374   FILE *f;\r
5375   char fileTitle[MSG_SIZ];\r
5376   f = OpenFileDialog(hwnd, "rb", "",\r
5377                      appData.oldSaveStyle ? "gam" : "pgn",\r
5378                      GAME_FILT,\r
5379                      title, &number, fileTitle, NULL);\r
5380   if (f != NULL) {\r
5381     cmailMsgLoaded = FALSE;\r
5382     if (number == 0) {\r
5383       int error = GameListBuild(f);\r
5384       if (error) {\r
5385         DisplayError("Cannot build game list", error);\r
5386       } else if (!ListEmpty(&gameList) &&\r
5387                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5388         GameListPopUp(f, fileTitle);\r
5389         return;\r
5390       }\r
5391       GameListDestroy();\r
5392       number = 1;\r
5393     }\r
5394     LoadGame(f, number, fileTitle, FALSE);\r
5395   }\r
5396 }\r
5397 \r
5398 VOID\r
5399 ChangedConsoleFont()\r
5400 {\r
5401   CHARFORMAT cfmt;\r
5402   CHARRANGE tmpsel, sel;\r
5403   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5404   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5405   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5406   PARAFORMAT paraf;\r
5407 \r
5408   cfmt.cbSize = sizeof(CHARFORMAT);\r
5409   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5410   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5411   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5412    * size.  This was undocumented in the version of MSVC++ that I had\r
5413    * when I wrote the code, but is apparently documented now.\r
5414    */\r
5415   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5416   cfmt.bCharSet = f->lf.lfCharSet;\r
5417   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5418   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5419   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5420   /* Why are the following seemingly needed too? */\r
5421   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5422   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5423   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5424   tmpsel.cpMin = 0;\r
5425   tmpsel.cpMax = -1; /*999999?*/\r
5426   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5427   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5428   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5429    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5430    */\r
5431   paraf.cbSize = sizeof(paraf);\r
5432   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5433   paraf.dxStartIndent = 0;\r
5434   paraf.dxOffset = WRAP_INDENT;\r
5435   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5436   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5437 }\r
5438 \r
5439 /*---------------------------------------------------------------------------*\\r
5440  *\r
5441  * Window Proc for main window\r
5442  *\r
5443 \*---------------------------------------------------------------------------*/\r
5444 \r
5445 /* Process messages for main window, etc. */\r
5446 LRESULT CALLBACK\r
5447 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5448 {\r
5449   FARPROC lpProc;\r
5450   int wmId, wmEvent;\r
5451   char *defName;\r
5452   FILE *f;\r
5453   UINT number;\r
5454   char fileTitle[MSG_SIZ];\r
5455   char buf[MSG_SIZ];\r
5456   static SnapData sd;\r
5457 \r
5458   switch (message) {\r
5459 \r
5460   case WM_PAINT: /* message: repaint portion of window */\r
5461     PaintProc(hwnd);\r
5462     break;\r
5463 \r
5464   case WM_ERASEBKGND:\r
5465     if (IsIconic(hwnd)) {\r
5466       /* Cheat; change the message */\r
5467       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5468     } else {\r
5469       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5470     }\r
5471     break;\r
5472 \r
5473   case WM_LBUTTONDOWN:\r
5474   case WM_MBUTTONDOWN:\r
5475   case WM_RBUTTONDOWN:\r
5476   case WM_LBUTTONUP:\r
5477   case WM_MBUTTONUP:\r
5478   case WM_RBUTTONUP:\r
5479   case WM_MOUSEMOVE:\r
5480   case WM_MOUSEWHEEL:\r
5481     MouseEvent(hwnd, message, wParam, lParam);\r
5482     break;\r
5483 \r
5484   case WM_CHAR:\r
5485     \r
5486     if (appData.icsActive) {\r
5487       if (wParam == '\t') {\r
5488         if (GetKeyState(VK_SHIFT) < 0) {\r
5489           /* shifted */\r
5490           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5491           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5492           SetFocus(h);\r
5493         } else {\r
5494           /* unshifted */\r
5495           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5496           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5497           SetFocus(h);\r
5498         }\r
5499       } else {\r
5500         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5501         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5502         SetFocus(h);\r
5503         SendMessage(h, message, wParam, lParam);\r
5504       }\r
5505     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5506       PopUpMoveDialog((char)wParam);\r
5507     }\r
5508     break;\r
5509 \r
5510   case WM_PALETTECHANGED:\r
5511     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5512       int nnew;\r
5513       HDC hdc = GetDC(hwndMain);\r
5514       SelectPalette(hdc, hPal, TRUE);\r
5515       nnew = RealizePalette(hdc);\r
5516       if (nnew > 0) {\r
5517         paletteChanged = TRUE;\r
5518 #if 0\r
5519         UpdateColors(hdc);\r
5520 #else\r
5521         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5522 #endif\r
5523       }\r
5524       ReleaseDC(hwnd, hdc);\r
5525     }\r
5526     break;\r
5527 \r
5528   case WM_QUERYNEWPALETTE:\r
5529     if (!appData.monoMode /*&& paletteChanged*/) {\r
5530       int nnew;\r
5531       HDC hdc = GetDC(hwndMain);\r
5532       paletteChanged = FALSE;\r
5533       SelectPalette(hdc, hPal, FALSE);\r
5534       nnew = RealizePalette(hdc);\r
5535       if (nnew > 0) {\r
5536         InvalidateRect(hwnd, &boardRect, FALSE);\r
5537       }\r
5538       ReleaseDC(hwnd, hdc);\r
5539       return TRUE;\r
5540     }\r
5541     return FALSE;\r
5542 \r
5543   case WM_COMMAND: /* message: command from application menu */\r
5544     wmId    = LOWORD(wParam);\r
5545     wmEvent = HIWORD(wParam);\r
5546 \r
5547     switch (wmId) {\r
5548     case IDM_NewGame:\r
5549       ResetGameEvent();\r
5550       AnalysisPopDown();\r
5551       break;\r
5552 \r
5553     case IDM_NewGameFRC:\r
5554       if( NewGameFRC() == 0 ) {\r
5555         ResetGameEvent();\r
5556         AnalysisPopDown();\r
5557       }\r
5558       break;\r
5559 \r
5560     case IDM_NewVariant:\r
5561       NewVariantPopup(hwnd);\r
5562       break;\r
5563 \r
5564     case IDM_LoadGame:\r
5565       LoadGameDialog(hwnd, "Load Game from File");\r
5566       break;\r
5567 \r
5568     case IDM_LoadNextGame:\r
5569       ReloadGame(1);\r
5570       break;\r
5571 \r
5572     case IDM_LoadPrevGame:\r
5573       ReloadGame(-1);\r
5574       break;\r
5575 \r
5576     case IDM_ReloadGame:\r
5577       ReloadGame(0);\r
5578       break;\r
5579 \r
5580     case IDM_LoadPosition:\r
5581       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5582         Reset(FALSE, TRUE);\r
5583       }\r
5584       number = 1;\r
5585       f = OpenFileDialog(hwnd, "rb", "",\r
5586                          appData.oldSaveStyle ? "pos" : "fen",\r
5587                          POSITION_FILT,\r
5588                          "Load Position from File", &number, fileTitle, NULL);\r
5589       if (f != NULL) {\r
5590         LoadPosition(f, number, fileTitle);\r
5591       }\r
5592       break;\r
5593 \r
5594     case IDM_LoadNextPosition:\r
5595       ReloadPosition(1);\r
5596       break;\r
5597 \r
5598     case IDM_LoadPrevPosition:\r
5599       ReloadPosition(-1);\r
5600       break;\r
5601 \r
5602     case IDM_ReloadPosition:\r
5603       ReloadPosition(0);\r
5604       break;\r
5605 \r
5606     case IDM_SaveGame:\r
5607       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5608       f = OpenFileDialog(hwnd, "a", defName,\r
5609                          appData.oldSaveStyle ? "gam" : "pgn",\r
5610                          GAME_FILT,\r
5611                          "Save Game to File", NULL, fileTitle, NULL);\r
5612       if (f != NULL) {\r
5613         SaveGame(f, 0, "");\r
5614       }\r
5615       break;\r
5616 \r
5617     case IDM_SavePosition:\r
5618       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5619       f = OpenFileDialog(hwnd, "a", defName,\r
5620                          appData.oldSaveStyle ? "pos" : "fen",\r
5621                          POSITION_FILT,\r
5622                          "Save Position to File", NULL, fileTitle, NULL);\r
5623       if (f != NULL) {\r
5624         SavePosition(f, 0, "");\r
5625       }\r
5626       break;\r
5627 \r
5628     case IDM_SaveDiagram:\r
5629       defName = "diagram";\r
5630       f = OpenFileDialog(hwnd, "wb", defName,\r
5631                          "bmp",\r
5632                          DIAGRAM_FILT,\r
5633                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5634       if (f != NULL) {\r
5635         SaveDiagram(f);\r
5636       }\r
5637       break;\r
5638 \r
5639     case IDM_CopyGame:\r
5640       CopyGameToClipboard();\r
5641       break;\r
5642 \r
5643     case IDM_PasteGame:\r
5644       PasteGameFromClipboard();\r
5645       break;\r
5646 \r
5647     case IDM_CopyGameListToClipboard:\r
5648       CopyGameListToClipboard();\r
5649       break;\r
5650 \r
5651     /* [AS] Autodetect FEN or PGN data */\r
5652     case IDM_PasteAny:\r
5653       PasteGameOrFENFromClipboard();\r
5654       break;\r
5655 \r
5656     /* [AS] Move history */\r
5657     case IDM_ShowMoveHistory:\r
5658         if( MoveHistoryIsUp() ) {\r
5659             MoveHistoryPopDown();\r
5660         }\r
5661         else {\r
5662             MoveHistoryPopUp();\r
5663         }\r
5664         break;\r
5665 \r
5666     /* [AS] Eval graph */\r
5667     case IDM_ShowEvalGraph:\r
5668         if( EvalGraphIsUp() ) {\r
5669             EvalGraphPopDown();\r
5670         }\r
5671         else {\r
5672             EvalGraphPopUp();\r
5673         }\r
5674         break;\r
5675 \r
5676     /* [AS] Engine output */\r
5677     case IDM_ShowEngineOutput:\r
5678         if( EngineOutputIsUp() ) {\r
5679             EngineOutputPopDown();\r
5680         }\r
5681         else {\r
5682             EngineOutputPopUp();\r
5683         }\r
5684         break;\r
5685 \r
5686     /* [AS] User adjudication */\r
5687     case IDM_UserAdjudication_White:\r
5688         UserAdjudicationEvent( +1 );\r
5689         break;\r
5690 \r
5691     case IDM_UserAdjudication_Black:\r
5692         UserAdjudicationEvent( -1 );\r
5693         break;\r
5694 \r
5695     case IDM_UserAdjudication_Draw:\r
5696         UserAdjudicationEvent( 0 );\r
5697         break;\r
5698 \r
5699     /* [AS] Game list options dialog */\r
5700     case IDM_GameListOptions:\r
5701       GameListOptions();\r
5702       break;\r
5703 \r
5704     case IDM_CopyPosition:\r
5705       CopyFENToClipboard();\r
5706       break;\r
5707 \r
5708     case IDM_PastePosition:\r
5709       PasteFENFromClipboard();\r
5710       break;\r
5711 \r
5712     case IDM_MailMove:\r
5713       MailMoveEvent();\r
5714       break;\r
5715 \r
5716     case IDM_ReloadCMailMsg:\r
5717       Reset(TRUE, TRUE);\r
5718       ReloadCmailMsgEvent(FALSE);\r
5719       break;\r
5720 \r
5721     case IDM_Minimize:\r
5722       ShowWindow(hwnd, SW_MINIMIZE);\r
5723       break;\r
5724 \r
5725     case IDM_Exit:\r
5726       ExitEvent(0);\r
5727       break;\r
5728 \r
5729     case IDM_MachineWhite:\r
5730       MachineWhiteEvent();\r
5731       /*\r
5732        * refresh the tags dialog only if it's visible\r
5733        */\r
5734       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5735           char *tags;\r
5736           tags = PGNTags(&gameInfo);\r
5737           TagsPopUp(tags, CmailMsg());\r
5738           free(tags);\r
5739       }\r
5740       break;\r
5741 \r
5742     case IDM_MachineBlack:\r
5743       MachineBlackEvent();\r
5744       /*\r
5745        * refresh the tags dialog only if it's visible\r
5746        */\r
5747       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5748           char *tags;\r
5749           tags = PGNTags(&gameInfo);\r
5750           TagsPopUp(tags, CmailMsg());\r
5751           free(tags);\r
5752       }\r
5753       break;\r
5754 \r
5755     case IDM_TwoMachines:\r
5756       TwoMachinesEvent();\r
5757       /*\r
5758        * refresh the tags dialog only if it's visible\r
5759        */\r
5760       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5761           char *tags;\r
5762           tags = PGNTags(&gameInfo);\r
5763           TagsPopUp(tags, CmailMsg());\r
5764           free(tags);\r
5765       }\r
5766       break;\r
5767 \r
5768     case IDM_AnalysisMode:\r
5769       if (!first.analysisSupport) {\r
5770         sprintf(buf, "%s does not support analysis", first.tidy);\r
5771         DisplayError(buf, 0);\r
5772       } else {\r
5773         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5774         if (appData.icsActive) {\r
5775                if (gameMode != IcsObserving) {\r
5776                        sprintf(buf, "You are not observing a game");\r
5777                        DisplayError(buf, 0);\r
5778                        /* secure check */\r
5779                        if (appData.icsEngineAnalyze) {\r
5780                                if (appData.debugMode) \r
5781                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5782                                ExitAnalyzeMode();\r
5783                                ModeHighlight();\r
5784                                break;\r
5785                        }\r
5786                        break;\r
5787                } else {\r
5788                        /* if enable, user want disable icsEngineAnalyze */\r
5789                        if (appData.icsEngineAnalyze) {\r
5790                                ExitAnalyzeMode();\r
5791                                ModeHighlight();\r
5792                                break;\r
5793                        }\r
5794                        appData.icsEngineAnalyze = TRUE;\r
5795                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5796                }\r
5797         } \r
5798         if (!appData.showThinking) ToggleShowThinking();\r
5799         AnalyzeModeEvent();\r
5800       }\r
5801       break;\r
5802 \r
5803     case IDM_AnalyzeFile:\r
5804       if (!first.analysisSupport) {\r
5805         char buf[MSG_SIZ];\r
5806         sprintf(buf, "%s does not support analysis", first.tidy);\r
5807         DisplayError(buf, 0);\r
5808       } else {\r
5809         if (!appData.showThinking) ToggleShowThinking();\r
5810         AnalyzeFileEvent();\r
5811         LoadGameDialog(hwnd, "Analyze Game from File");\r
5812         AnalysisPeriodicEvent(1);\r
5813       }\r
5814       break;\r
5815 \r
5816     case IDM_IcsClient:\r
5817       IcsClientEvent();\r
5818       break;\r
5819 \r
5820     case IDM_EditGame:\r
5821       EditGameEvent();\r
5822       break;\r
5823 \r
5824     case IDM_EditPosition:\r
5825       EditPositionEvent();\r
5826       break;\r
5827 \r
5828     case IDM_Training:\r
5829       TrainingEvent();\r
5830       break;\r
5831 \r
5832     case IDM_ShowGameList:\r
5833       ShowGameListProc();\r
5834       break;\r
5835 \r
5836     case IDM_EditTags:\r
5837       EditTagsProc();\r
5838       break;\r
5839 \r
5840     case IDM_EditComment:\r
5841       if (commentDialogUp && editComment) {\r
5842         CommentPopDown();\r
5843       } else {\r
5844         EditCommentEvent();\r
5845       }\r
5846       break;\r
5847 \r
5848     case IDM_Pause:\r
5849       PauseEvent();\r
5850       break;\r
5851 \r
5852     case IDM_Accept:\r
5853       AcceptEvent();\r
5854       break;\r
5855 \r
5856     case IDM_Decline:\r
5857       DeclineEvent();\r
5858       break;\r
5859 \r
5860     case IDM_Rematch:\r
5861       RematchEvent();\r
5862       break;\r
5863 \r
5864     case IDM_CallFlag:\r
5865       CallFlagEvent();\r
5866       break;\r
5867 \r
5868     case IDM_Draw:\r
5869       DrawEvent();\r
5870       break;\r
5871 \r
5872     case IDM_Adjourn:\r
5873       AdjournEvent();\r
5874       break;\r
5875 \r
5876     case IDM_Abort:\r
5877       AbortEvent();\r
5878       break;\r
5879 \r
5880     case IDM_Resign:\r
5881       ResignEvent();\r
5882       break;\r
5883 \r
5884     case IDM_StopObserving:\r
5885       StopObservingEvent();\r
5886       break;\r
5887 \r
5888     case IDM_StopExamining:\r
5889       StopExaminingEvent();\r
5890       break;\r
5891 \r
5892     case IDM_TypeInMove:\r
5893       PopUpMoveDialog('\000');\r
5894       break;\r
5895 \r
5896     case IDM_TypeInName:\r
5897       PopUpNameDialog('\000');\r
5898       break;\r
5899 \r
5900     case IDM_Backward:\r
5901       BackwardEvent();\r
5902       SetFocus(hwndMain);\r
5903       break;\r
5904 \r
5905     case IDM_Forward:\r
5906       ForwardEvent();\r
5907       SetFocus(hwndMain);\r
5908       break;\r
5909 \r
5910     case IDM_ToStart:\r
5911       ToStartEvent();\r
5912       SetFocus(hwndMain);\r
5913       break;\r
5914 \r
5915     case IDM_ToEnd:\r
5916       ToEndEvent();\r
5917       SetFocus(hwndMain);\r
5918       break;\r
5919 \r
5920     case IDM_Revert:\r
5921       RevertEvent();\r
5922       break;\r
5923 \r
5924     case IDM_TruncateGame:\r
5925       TruncateGameEvent();\r
5926       break;\r
5927 \r
5928     case IDM_MoveNow:\r
5929       MoveNowEvent();\r
5930       break;\r
5931 \r
5932     case IDM_RetractMove:\r
5933       RetractMoveEvent();\r
5934       break;\r
5935 \r
5936     case IDM_FlipView:\r
5937       flipView = !flipView;\r
5938       DrawPosition(FALSE, NULL);\r
5939       break;\r
5940 \r
5941     case IDM_FlipClock:\r
5942       flipClock = !flipClock;\r
5943       DisplayBothClocks();\r
5944       DrawPosition(FALSE, NULL);\r
5945       break;\r
5946 \r
5947     case IDM_GeneralOptions:\r
5948       GeneralOptionsPopup(hwnd);\r
5949       DrawPosition(TRUE, NULL);\r
5950       break;\r
5951 \r
5952     case IDM_BoardOptions:\r
5953       BoardOptionsPopup(hwnd);\r
5954       break;\r
5955 \r
5956     case IDM_EnginePlayOptions:\r
5957       EnginePlayOptionsPopup(hwnd);\r
5958       break;\r
5959 \r
5960     case IDM_OptionsUCI:\r
5961       UciOptionsPopup(hwnd);\r
5962       break;\r
5963 \r
5964     case IDM_IcsOptions:\r
5965       IcsOptionsPopup(hwnd);\r
5966       break;\r
5967 \r
5968     case IDM_Fonts:\r
5969       FontsOptionsPopup(hwnd);\r
5970       break;\r
5971 \r
5972     case IDM_Sounds:\r
5973       SoundOptionsPopup(hwnd);\r
5974       break;\r
5975 \r
5976     case IDM_CommPort:\r
5977       CommPortOptionsPopup(hwnd);\r
5978       break;\r
5979 \r
5980     case IDM_LoadOptions:\r
5981       LoadOptionsPopup(hwnd);\r
5982       break;\r
5983 \r
5984     case IDM_SaveOptions:\r
5985       SaveOptionsPopup(hwnd);\r
5986       break;\r
5987 \r
5988     case IDM_TimeControl:\r
5989       TimeControlOptionsPopup(hwnd);\r
5990       break;\r
5991 \r
5992     case IDM_SaveSettings:\r
5993       SaveSettings(settingsFileName);\r
5994       break;\r
5995 \r
5996     case IDM_SaveSettingsOnExit:\r
5997       saveSettingsOnExit = !saveSettingsOnExit;\r
5998       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5999                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6000                                          MF_CHECKED : MF_UNCHECKED));\r
6001       break;\r
6002 \r
6003     case IDM_Hint:\r
6004       HintEvent();\r
6005       break;\r
6006 \r
6007     case IDM_Book:\r
6008       BookEvent();\r
6009       break;\r
6010 \r
6011     case IDM_AboutGame:\r
6012       AboutGameEvent();\r
6013       break;\r
6014 \r
6015     case IDM_Debug:\r
6016       appData.debugMode = !appData.debugMode;\r
6017       if (appData.debugMode) {\r
6018         char dir[MSG_SIZ];\r
6019         GetCurrentDirectory(MSG_SIZ, dir);\r
6020         SetCurrentDirectory(installDir);\r
6021         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6022         SetCurrentDirectory(dir);\r
6023         setbuf(debugFP, NULL);\r
6024       } else {\r
6025         fclose(debugFP);\r
6026         debugFP = NULL;\r
6027       }\r
6028       break;\r
6029 \r
6030     case IDM_HELPCONTENTS:\r
6031       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\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_HELPSEARCH:\r
6039       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\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_HELPHELP:\r
6047       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6048         MessageBox (GetFocus(),\r
6049                     "Unable to activate help",\r
6050                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6051       }\r
6052       break;\r
6053 \r
6054     case IDM_ABOUT:\r
6055       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6056       DialogBox(hInst, \r
6057         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6058         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6059       FreeProcInstance(lpProc);\r
6060       break;\r
6061 \r
6062     case IDM_DirectCommand1:\r
6063       AskQuestionEvent("Direct Command",\r
6064                        "Send to chess program:", "", "1");\r
6065       break;\r
6066     case IDM_DirectCommand2:\r
6067       AskQuestionEvent("Direct Command",\r
6068                        "Send to second chess program:", "", "2");\r
6069       break;\r
6070 \r
6071     case EP_WhitePawn:\r
6072       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6073       fromX = fromY = -1;\r
6074       break;\r
6075 \r
6076     case EP_WhiteKnight:\r
6077       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6078       fromX = fromY = -1;\r
6079       break;\r
6080 \r
6081     case EP_WhiteBishop:\r
6082       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6083       fromX = fromY = -1;\r
6084       break;\r
6085 \r
6086     case EP_WhiteRook:\r
6087       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6088       fromX = fromY = -1;\r
6089       break;\r
6090 \r
6091     case EP_WhiteQueen:\r
6092       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6093       fromX = fromY = -1;\r
6094       break;\r
6095 \r
6096     case EP_WhiteFerz:\r
6097       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6098       fromX = fromY = -1;\r
6099       break;\r
6100 \r
6101     case EP_WhiteWazir:\r
6102       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6103       fromX = fromY = -1;\r
6104       break;\r
6105 \r
6106     case EP_WhiteAlfil:\r
6107       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6108       fromX = fromY = -1;\r
6109       break;\r
6110 \r
6111     case EP_WhiteCannon:\r
6112       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6113       fromX = fromY = -1;\r
6114       break;\r
6115 \r
6116     case EP_WhiteCardinal:\r
6117       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6118       fromX = fromY = -1;\r
6119       break;\r
6120 \r
6121     case EP_WhiteMarshall:\r
6122       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6123       fromX = fromY = -1;\r
6124       break;\r
6125 \r
6126     case EP_WhiteKing:\r
6127       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6128       fromX = fromY = -1;\r
6129       break;\r
6130 \r
6131     case EP_BlackPawn:\r
6132       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6133       fromX = fromY = -1;\r
6134       break;\r
6135 \r
6136     case EP_BlackKnight:\r
6137       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6138       fromX = fromY = -1;\r
6139       break;\r
6140 \r
6141     case EP_BlackBishop:\r
6142       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6143       fromX = fromY = -1;\r
6144       break;\r
6145 \r
6146     case EP_BlackRook:\r
6147       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6148       fromX = fromY = -1;\r
6149       break;\r
6150 \r
6151     case EP_BlackQueen:\r
6152       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6153       fromX = fromY = -1;\r
6154       break;\r
6155 \r
6156     case EP_BlackFerz:\r
6157       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6158       fromX = fromY = -1;\r
6159       break;\r
6160 \r
6161     case EP_BlackWazir:\r
6162       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6163       fromX = fromY = -1;\r
6164       break;\r
6165 \r
6166     case EP_BlackAlfil:\r
6167       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6168       fromX = fromY = -1;\r
6169       break;\r
6170 \r
6171     case EP_BlackCannon:\r
6172       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6173       fromX = fromY = -1;\r
6174       break;\r
6175 \r
6176     case EP_BlackCardinal:\r
6177       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6178       fromX = fromY = -1;\r
6179       break;\r
6180 \r
6181     case EP_BlackMarshall:\r
6182       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6183       fromX = fromY = -1;\r
6184       break;\r
6185 \r
6186     case EP_BlackKing:\r
6187       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6188       fromX = fromY = -1;\r
6189       break;\r
6190 \r
6191     case EP_EmptySquare:\r
6192       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6193       fromX = fromY = -1;\r
6194       break;\r
6195 \r
6196     case EP_ClearBoard:\r
6197       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6198       fromX = fromY = -1;\r
6199       break;\r
6200 \r
6201     case EP_White:\r
6202       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6203       fromX = fromY = -1;\r
6204       break;\r
6205 \r
6206     case EP_Black:\r
6207       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6208       fromX = fromY = -1;\r
6209       break;\r
6210 \r
6211     case EP_Promote:\r
6212       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6213       fromX = fromY = -1;\r
6214       break;\r
6215 \r
6216     case EP_Demote:\r
6217       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6218       fromX = fromY = -1;\r
6219       break;\r
6220 \r
6221     case DP_Pawn:\r
6222       DropMenuEvent(WhitePawn, fromX, fromY);\r
6223       fromX = fromY = -1;\r
6224       break;\r
6225 \r
6226     case DP_Knight:\r
6227       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6228       fromX = fromY = -1;\r
6229       break;\r
6230 \r
6231     case DP_Bishop:\r
6232       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6233       fromX = fromY = -1;\r
6234       break;\r
6235 \r
6236     case DP_Rook:\r
6237       DropMenuEvent(WhiteRook, fromX, fromY);\r
6238       fromX = fromY = -1;\r
6239       break;\r
6240 \r
6241     case DP_Queen:\r
6242       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6243       fromX = fromY = -1;\r
6244       break;\r
6245 \r
6246     default:\r
6247       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6248     }\r
6249     break;\r
6250 \r
6251   case WM_TIMER:\r
6252     switch (wParam) {\r
6253     case CLOCK_TIMER_ID:\r
6254       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6255       clockTimerEvent = 0;\r
6256       DecrementClocks(); /* call into back end */\r
6257       break;\r
6258     case LOAD_GAME_TIMER_ID:\r
6259       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6260       loadGameTimerEvent = 0;\r
6261       AutoPlayGameLoop(); /* call into back end */\r
6262       break;\r
6263     case ANALYSIS_TIMER_ID:\r
6264       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6265                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6266         AnalysisPeriodicEvent(0);\r
6267       } else {\r
6268         KillTimer(hwnd, analysisTimerEvent);\r
6269         analysisTimerEvent = 0;\r
6270       }\r
6271       break;\r
6272     case DELAYED_TIMER_ID:\r
6273       KillTimer(hwnd, delayedTimerEvent);\r
6274       delayedTimerEvent = 0;\r
6275       delayedTimerCallback();\r
6276       break;\r
6277     }\r
6278     break;\r
6279 \r
6280   case WM_USER_Input:\r
6281     InputEvent(hwnd, message, wParam, lParam);\r
6282     break;\r
6283 \r
6284   /* [AS] Also move "attached" child windows */\r
6285   case WM_WINDOWPOSCHANGING:\r
6286 \r
6287     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6288         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6289 \r
6290         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6291             /* Window is moving */\r
6292             RECT rcMain;\r
6293 \r
6294 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6295             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6296             rcMain.right  = boardX + winWidth;\r
6297             rcMain.top    = boardY;\r
6298             rcMain.bottom = boardY + winHeight;\r
6299             \r
6300             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6301             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6302             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6303             boardX = lpwp->x;\r
6304             boardY = lpwp->y;\r
6305         }\r
6306     }\r
6307     break;\r
6308 \r
6309   /* [AS] Snapping */\r
6310   case WM_ENTERSIZEMOVE:\r
6311     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6312     if (hwnd == hwndMain) {\r
6313       doingSizing = TRUE;\r
6314       lastSizing = 0;\r
6315     }\r
6316     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6317     break;\r
6318 \r
6319   case WM_SIZING:\r
6320     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6321     if (hwnd == hwndMain) {\r
6322       lastSizing = wParam;\r
6323     }\r
6324     break;\r
6325 \r
6326   case WM_MOVING:\r
6327     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6328       return OnMoving( &sd, hwnd, wParam, lParam );\r
6329 \r
6330   case WM_EXITSIZEMOVE:\r
6331     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6332     if (hwnd == hwndMain) {\r
6333       RECT client;\r
6334       doingSizing = FALSE;\r
6335       InvalidateRect(hwnd, &boardRect, FALSE);\r
6336       GetClientRect(hwnd, &client);\r
6337       ResizeBoard(client.right, client.bottom, lastSizing);\r
6338       lastSizing = 0;\r
6339       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6340     }\r
6341     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6342     break;\r
6343 \r
6344   case WM_DESTROY: /* message: window being destroyed */\r
6345     PostQuitMessage(0);\r
6346     break;\r
6347 \r
6348   case WM_CLOSE:\r
6349     if (hwnd == hwndMain) {\r
6350       ExitEvent(0);\r
6351     }\r
6352     break;\r
6353 \r
6354   default:      /* Passes it on if unprocessed */\r
6355     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6356   }\r
6357   return 0;\r
6358 }\r
6359 \r
6360 /*---------------------------------------------------------------------------*\\r
6361  *\r
6362  * Misc utility routines\r
6363  *\r
6364 \*---------------------------------------------------------------------------*/\r
6365 \r
6366 /*\r
6367  * Decent random number generator, at least not as bad as Windows\r
6368  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6369  */\r
6370 unsigned int randstate;\r
6371 \r
6372 int\r
6373 myrandom(void)\r
6374 {\r
6375   randstate = randstate * 1664525 + 1013904223;\r
6376   return (int) randstate & 0x7fffffff;\r
6377 }\r
6378 \r
6379 void\r
6380 mysrandom(unsigned int seed)\r
6381 {\r
6382   randstate = seed;\r
6383 }\r
6384 \r
6385 \r
6386 /* \r
6387  * returns TRUE if user selects a different color, FALSE otherwise \r
6388  */\r
6389 \r
6390 BOOL\r
6391 ChangeColor(HWND hwnd, COLORREF *which)\r
6392 {\r
6393   static BOOL firstTime = TRUE;\r
6394   static DWORD customColors[16];\r
6395   CHOOSECOLOR cc;\r
6396   COLORREF newcolor;\r
6397   int i;\r
6398   ColorClass ccl;\r
6399 \r
6400   if (firstTime) {\r
6401     /* Make initial colors in use available as custom colors */\r
6402     /* Should we put the compiled-in defaults here instead? */\r
6403     i = 0;\r
6404     customColors[i++] = lightSquareColor & 0xffffff;\r
6405     customColors[i++] = darkSquareColor & 0xffffff;\r
6406     customColors[i++] = whitePieceColor & 0xffffff;\r
6407     customColors[i++] = blackPieceColor & 0xffffff;\r
6408     customColors[i++] = highlightSquareColor & 0xffffff;\r
6409     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6410 \r
6411     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6412       customColors[i++] = textAttribs[ccl].color;\r
6413     }\r
6414     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6415     firstTime = FALSE;\r
6416   }\r
6417 \r
6418   cc.lStructSize = sizeof(cc);\r
6419   cc.hwndOwner = hwnd;\r
6420   cc.hInstance = NULL;\r
6421   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6422   cc.lpCustColors = (LPDWORD) customColors;\r
6423   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6424 \r
6425   if (!ChooseColor(&cc)) return FALSE;\r
6426 \r
6427   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6428   if (newcolor == *which) return FALSE;\r
6429   *which = newcolor;\r
6430   return TRUE;\r
6431 \r
6432   /*\r
6433   InitDrawingColors();\r
6434   InvalidateRect(hwnd, &boardRect, FALSE);\r
6435   */\r
6436 }\r
6437 \r
6438 BOOLEAN\r
6439 MyLoadSound(MySound *ms)\r
6440 {\r
6441   BOOL ok = FALSE;\r
6442   struct stat st;\r
6443   FILE *f;\r
6444 \r
6445   if (ms->data) free(ms->data);\r
6446   ms->data = NULL;\r
6447 \r
6448   switch (ms->name[0]) {\r
6449   case NULLCHAR:\r
6450     /* Silence */\r
6451     ok = TRUE;\r
6452     break;\r
6453   case '$':\r
6454     /* System sound from Control Panel.  Don't preload here. */\r
6455     ok = TRUE;\r
6456     break;\r
6457   case '!':\r
6458     if (ms->name[1] == NULLCHAR) {\r
6459       /* "!" alone = silence */\r
6460       ok = TRUE;\r
6461     } else {\r
6462       /* Builtin wave resource.  Error if not found. */\r
6463       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6464       if (h == NULL) break;\r
6465       ms->data = (void *)LoadResource(hInst, h);\r
6466       if (h == NULL) break;\r
6467       ok = TRUE;\r
6468     }\r
6469     break;\r
6470   default:\r
6471     /* .wav file.  Error if not found. */\r
6472     f = fopen(ms->name, "rb");\r
6473     if (f == NULL) break;\r
6474     if (fstat(fileno(f), &st) < 0) break;\r
6475     ms->data = malloc(st.st_size);\r
6476     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6477     fclose(f);\r
6478     ok = TRUE;\r
6479     break;\r
6480   }\r
6481   if (!ok) {\r
6482     char buf[MSG_SIZ];\r
6483     sprintf(buf, "Error loading sound %s", ms->name);\r
6484     DisplayError(buf, GetLastError());\r
6485   }\r
6486   return ok;\r
6487 }\r
6488 \r
6489 BOOLEAN\r
6490 MyPlaySound(MySound *ms)\r
6491 {\r
6492   BOOLEAN ok = FALSE;\r
6493   switch (ms->name[0]) {\r
6494   case NULLCHAR:\r
6495     /* Silence */\r
6496     ok = TRUE;\r
6497     break;\r
6498   case '$':\r
6499     /* System sound from Control Panel (deprecated feature).\r
6500        "$" alone or an unset sound name gets default beep (still in use). */\r
6501     if (ms->name[1]) {\r
6502       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6503     }\r
6504     if (!ok) ok = MessageBeep(MB_OK);\r
6505     break; \r
6506   case '!':\r
6507     /* Builtin wave resource, or "!" alone for silence */\r
6508     if (ms->name[1]) {\r
6509       if (ms->data == NULL) return FALSE;\r
6510       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6511     } else {\r
6512       ok = TRUE;\r
6513     }\r
6514     break;\r
6515   default:\r
6516     /* .wav file.  Error if not found. */\r
6517     if (ms->data == NULL) return FALSE;\r
6518     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6519     break;\r
6520   }\r
6521   /* Don't print an error: this can happen innocently if the sound driver\r
6522      is busy; for instance, if another instance of WinBoard is playing\r
6523      a sound at about the same time. */\r
6524 #if 0\r
6525   if (!ok) {\r
6526     char buf[MSG_SIZ];\r
6527     sprintf(buf, "Error playing sound %s", ms->name);\r
6528     DisplayError(buf, GetLastError());\r
6529   }\r
6530 #endif\r
6531   return ok;\r
6532 }\r
6533 \r
6534 \r
6535 LRESULT CALLBACK\r
6536 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6537 {\r
6538   BOOL ok;\r
6539   OPENFILENAME *ofn;\r
6540   static UINT *number; /* gross that this is static */\r
6541 \r
6542   switch (message) {\r
6543   case WM_INITDIALOG: /* message: initialize dialog box */\r
6544     /* Center the dialog over the application window */\r
6545     ofn = (OPENFILENAME *) lParam;\r
6546     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6547       number = (UINT *) ofn->lCustData;\r
6548       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6549     } else {\r
6550       number = NULL;\r
6551     }\r
6552     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6553     return FALSE;  /* Allow for further processing */\r
6554 \r
6555   case WM_COMMAND:\r
6556     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6557       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6558     }\r
6559     return FALSE;  /* Allow for further processing */\r
6560   }\r
6561   return FALSE;\r
6562 }\r
6563 \r
6564 UINT APIENTRY\r
6565 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6566 {\r
6567   static UINT *number;\r
6568   OPENFILENAME *ofname;\r
6569   OFNOTIFY *ofnot;\r
6570   switch (uiMsg) {\r
6571   case WM_INITDIALOG:\r
6572     ofname = (OPENFILENAME *)lParam;\r
6573     number = (UINT *)(ofname->lCustData);\r
6574     break;\r
6575   case WM_NOTIFY:\r
6576     ofnot = (OFNOTIFY *)lParam;\r
6577     if (ofnot->hdr.code == CDN_FILEOK) {\r
6578       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6579     }\r
6580     break;\r
6581   }\r
6582   return 0;\r
6583 }\r
6584 \r
6585 \r
6586 FILE *\r
6587 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6588                char *nameFilt, char *dlgTitle, UINT *number,\r
6589                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6590 {\r
6591   OPENFILENAME openFileName;\r
6592   char buf1[MSG_SIZ];\r
6593   FILE *f;\r
6594 \r
6595   if (fileName == NULL) fileName = buf1;\r
6596   if (defName == NULL) {\r
6597     strcpy(fileName, "*.");\r
6598     strcat(fileName, defExt);\r
6599   } else {\r
6600     strcpy(fileName, defName);\r
6601   }\r
6602   if (fileTitle) strcpy(fileTitle, "");\r
6603   if (number) *number = 0;\r
6604 \r
6605   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6606   openFileName.hwndOwner         = hwnd;\r
6607   openFileName.hInstance         = (HANDLE) hInst;\r
6608   openFileName.lpstrFilter       = nameFilt;\r
6609   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6610   openFileName.nMaxCustFilter    = 0L;\r
6611   openFileName.nFilterIndex      = 1L;\r
6612   openFileName.lpstrFile         = fileName;\r
6613   openFileName.nMaxFile          = MSG_SIZ;\r
6614   openFileName.lpstrFileTitle    = fileTitle;\r
6615   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6616   openFileName.lpstrInitialDir   = NULL;\r
6617   openFileName.lpstrTitle        = dlgTitle;\r
6618   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6619     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6620     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6621     | (oldDialog ? 0 : OFN_EXPLORER);\r
6622   openFileName.nFileOffset       = 0;\r
6623   openFileName.nFileExtension    = 0;\r
6624   openFileName.lpstrDefExt       = defExt;\r
6625   openFileName.lCustData         = (LONG) number;\r
6626   openFileName.lpfnHook          = oldDialog ?\r
6627     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6628   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6629 \r
6630   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6631                         GetOpenFileName(&openFileName)) {\r
6632     /* open the file */\r
6633     f = fopen(openFileName.lpstrFile, write);\r
6634     if (f == NULL) {\r
6635       MessageBox(hwnd, "File open failed", NULL,\r
6636                  MB_OK|MB_ICONEXCLAMATION);\r
6637       return NULL;\r
6638     }\r
6639   } else {\r
6640     int err = CommDlgExtendedError();\r
6641     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6642     return FALSE;\r
6643   }\r
6644   return f;\r
6645 }\r
6646 \r
6647 \r
6648 \r
6649 VOID APIENTRY\r
6650 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6651 {\r
6652   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6653 \r
6654   /*\r
6655    * Get the first pop-up menu in the menu template. This is the\r
6656    * menu that TrackPopupMenu displays.\r
6657    */\r
6658   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6659 \r
6660   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6661 \r
6662   /*\r
6663    * TrackPopup uses screen coordinates, so convert the\r
6664    * coordinates of the mouse click to screen coordinates.\r
6665    */\r
6666   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6667 \r
6668   /* Draw and track the floating pop-up menu. */\r
6669   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6670                  pt.x, pt.y, 0, hwnd, NULL);\r
6671 \r
6672   /* Destroy the menu.*/\r
6673   DestroyMenu(hmenu);\r
6674 }\r
6675    \r
6676 typedef struct {\r
6677   HWND hDlg, hText;\r
6678   int sizeX, sizeY, newSizeX, newSizeY;\r
6679   HDWP hdwp;\r
6680 } ResizeEditPlusButtonsClosure;\r
6681 \r
6682 BOOL CALLBACK\r
6683 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6684 {\r
6685   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6686   RECT rect;\r
6687   POINT pt;\r
6688 \r
6689   if (hChild == cl->hText) return TRUE;\r
6690   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6691   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6692   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6693   ScreenToClient(cl->hDlg, &pt);\r
6694   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6695     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6696   return TRUE;\r
6697 }\r
6698 \r
6699 /* Resize a dialog that has a (rich) edit field filling most of\r
6700    the top, with a row of buttons below */\r
6701 VOID\r
6702 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6703 {\r
6704   RECT rectText;\r
6705   int newTextHeight, newTextWidth;\r
6706   ResizeEditPlusButtonsClosure cl;\r
6707   \r
6708   /*if (IsIconic(hDlg)) return;*/\r
6709   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6710   \r
6711   cl.hdwp = BeginDeferWindowPos(8);\r
6712 \r
6713   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6714   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6715   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6716   if (newTextHeight < 0) {\r
6717     newSizeY += -newTextHeight;\r
6718     newTextHeight = 0;\r
6719   }\r
6720   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6721     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6722 \r
6723   cl.hDlg = hDlg;\r
6724   cl.hText = hText;\r
6725   cl.sizeX = sizeX;\r
6726   cl.sizeY = sizeY;\r
6727   cl.newSizeX = newSizeX;\r
6728   cl.newSizeY = newSizeY;\r
6729   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6730 \r
6731   EndDeferWindowPos(cl.hdwp);\r
6732 }\r
6733 \r
6734 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6735 {\r
6736     RECT    rChild, rParent;\r
6737     int     wChild, hChild, wParent, hParent;\r
6738     int     wScreen, hScreen, xNew, yNew;\r
6739     HDC     hdc;\r
6740 \r
6741     /* Get the Height and Width of the child window */\r
6742     GetWindowRect (hwndChild, &rChild);\r
6743     wChild = rChild.right - rChild.left;\r
6744     hChild = rChild.bottom - rChild.top;\r
6745 \r
6746     /* Get the Height and Width of the parent window */\r
6747     GetWindowRect (hwndParent, &rParent);\r
6748     wParent = rParent.right - rParent.left;\r
6749     hParent = rParent.bottom - rParent.top;\r
6750 \r
6751     /* Get the display limits */\r
6752     hdc = GetDC (hwndChild);\r
6753     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6754     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6755     ReleaseDC(hwndChild, hdc);\r
6756 \r
6757     /* Calculate new X position, then adjust for screen */\r
6758     xNew = rParent.left + ((wParent - wChild) /2);\r
6759     if (xNew < 0) {\r
6760         xNew = 0;\r
6761     } else if ((xNew+wChild) > wScreen) {\r
6762         xNew = wScreen - wChild;\r
6763     }\r
6764 \r
6765     /* Calculate new Y position, then adjust for screen */\r
6766     if( mode == 0 ) {\r
6767         yNew = rParent.top  + ((hParent - hChild) /2);\r
6768     }\r
6769     else {\r
6770         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6771     }\r
6772 \r
6773     if (yNew < 0) {\r
6774         yNew = 0;\r
6775     } else if ((yNew+hChild) > hScreen) {\r
6776         yNew = hScreen - hChild;\r
6777     }\r
6778 \r
6779     /* Set it, and return */\r
6780     return SetWindowPos (hwndChild, NULL,\r
6781                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6782 }\r
6783 \r
6784 /* Center one window over another */\r
6785 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6786 {\r
6787     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6788 }\r
6789 \r
6790 /*---------------------------------------------------------------------------*\\r
6791  *\r
6792  * Startup Dialog functions\r
6793  *\r
6794 \*---------------------------------------------------------------------------*/\r
6795 void\r
6796 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6797 {\r
6798   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6799 \r
6800   while (*cd != NULL) {\r
6801     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6802     cd++;\r
6803   }\r
6804 }\r
6805 \r
6806 void\r
6807 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6808 {\r
6809   char buf1[ARG_MAX];\r
6810   int len;\r
6811 \r
6812   if (str[0] == '@') {\r
6813     FILE* f = fopen(str + 1, "r");\r
6814     if (f == NULL) {\r
6815       DisplayFatalError(str + 1, errno, 2);\r
6816       return;\r
6817     }\r
6818     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6819     fclose(f);\r
6820     buf1[len] = NULLCHAR;\r
6821     str = buf1;\r
6822   }\r
6823 \r
6824   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6825 \r
6826   for (;;) {\r
6827     char buf[MSG_SIZ];\r
6828     char *end = strchr(str, '\n');\r
6829     if (end == NULL) return;\r
6830     memcpy(buf, str, end - str);\r
6831     buf[end - str] = NULLCHAR;\r
6832     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6833     str = end + 1;\r
6834   }\r
6835 }\r
6836 \r
6837 void\r
6838 SetStartupDialogEnables(HWND hDlg)\r
6839 {\r
6840   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6841     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6842     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6843   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6844     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6845   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6846     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6847   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6848     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6849   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6850     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6851     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6852     IsDlgButtonChecked(hDlg, OPT_View));\r
6853 }\r
6854 \r
6855 char *\r
6856 QuoteForFilename(char *filename)\r
6857 {\r
6858   int dquote, space;\r
6859   dquote = strchr(filename, '"') != NULL;\r
6860   space = strchr(filename, ' ') != NULL;\r
6861   if (dquote || space) {\r
6862     if (dquote) {\r
6863       return "'";\r
6864     } else {\r
6865       return "\"";\r
6866     }\r
6867   } else {\r
6868     return "";\r
6869   }\r
6870 }\r
6871 \r
6872 VOID\r
6873 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6874 {\r
6875   char buf[MSG_SIZ];\r
6876   char *q;\r
6877 \r
6878   InitComboStringsFromOption(hwndCombo, nthnames);\r
6879   q = QuoteForFilename(nthcp);\r
6880   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6881   if (*nthdir != NULLCHAR) {\r
6882     q = QuoteForFilename(nthdir);\r
6883     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6884   }\r
6885   if (*nthcp == NULLCHAR) {\r
6886     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6887   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6888     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6889     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6890   }\r
6891 }\r
6892 \r
6893 LRESULT CALLBACK\r
6894 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6895 {\r
6896   char buf[MSG_SIZ];\r
6897   HANDLE hwndCombo;\r
6898   char *p;\r
6899 \r
6900   switch (message) {\r
6901   case WM_INITDIALOG:\r
6902     /* Center the dialog */\r
6903     CenterWindow (hDlg, GetDesktopWindow());\r
6904     /* Initialize the dialog items */\r
6905     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6906                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6907                   firstChessProgramNames);\r
6908     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6909                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6910                   secondChessProgramNames);\r
6911     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6912     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6913     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6914     if (*appData.icsHelper != NULLCHAR) {\r
6915       char *q = QuoteForFilename(appData.icsHelper);\r
6916       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6917     }\r
6918     if (*appData.icsHost == NULLCHAR) {\r
6919       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6920       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6921     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6922       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6923       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6924     }\r
6925 \r
6926     if (appData.icsActive) {\r
6927       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6928     }\r
6929     else if (appData.noChessProgram) {\r
6930       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6931     }\r
6932     else {\r
6933       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6934     }\r
6935 \r
6936     SetStartupDialogEnables(hDlg);\r
6937     return TRUE;\r
6938 \r
6939   case WM_COMMAND:\r
6940     switch (LOWORD(wParam)) {\r
6941     case IDOK:\r
6942       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6943         strcpy(buf, "/fcp=");\r
6944         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6945         p = buf;\r
6946         ParseArgs(StringGet, &p);\r
6947         strcpy(buf, "/scp=");\r
6948         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6949         p = buf;\r
6950         ParseArgs(StringGet, &p);\r
6951         appData.noChessProgram = FALSE;\r
6952         appData.icsActive = FALSE;\r
6953       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6954         strcpy(buf, "/ics /icshost=");\r
6955         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6956         p = buf;\r
6957         ParseArgs(StringGet, &p);\r
6958         if (appData.zippyPlay) {\r
6959           strcpy(buf, "/fcp=");\r
6960           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6961           p = buf;\r
6962           ParseArgs(StringGet, &p);\r
6963         }\r
6964       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6965         appData.noChessProgram = TRUE;\r
6966         appData.icsActive = FALSE;\r
6967       } else {\r
6968         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6969                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6970         return TRUE;\r
6971       }\r
6972       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6973         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6974         p = buf;\r
6975         ParseArgs(StringGet, &p);\r
6976       }\r
6977       EndDialog(hDlg, TRUE);\r
6978       return TRUE;\r
6979 \r
6980     case IDCANCEL:\r
6981       ExitEvent(0);\r
6982       return TRUE;\r
6983 \r
6984     case IDM_HELPCONTENTS:\r
6985       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6986         MessageBox (GetFocus(),\r
6987                     "Unable to activate help",\r
6988                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6989       }\r
6990       break;\r
6991 \r
6992     default:\r
6993       SetStartupDialogEnables(hDlg);\r
6994       break;\r
6995     }\r
6996     break;\r
6997   }\r
6998   return FALSE;\r
6999 }\r
7000 \r
7001 /*---------------------------------------------------------------------------*\\r
7002  *\r
7003  * About box dialog functions\r
7004  *\r
7005 \*---------------------------------------------------------------------------*/\r
7006 \r
7007 /* Process messages for "About" dialog box */\r
7008 LRESULT CALLBACK\r
7009 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7010 {\r
7011   switch (message) {\r
7012   case WM_INITDIALOG: /* message: initialize dialog box */\r
7013     /* Center the dialog over the application window */\r
7014     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7015     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7016     return (TRUE);\r
7017 \r
7018   case WM_COMMAND: /* message: received a command */\r
7019     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7020         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7021       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7022       return (TRUE);\r
7023     }\r
7024     break;\r
7025   }\r
7026   return (FALSE);\r
7027 }\r
7028 \r
7029 /*---------------------------------------------------------------------------*\\r
7030  *\r
7031  * Comment Dialog functions\r
7032  *\r
7033 \*---------------------------------------------------------------------------*/\r
7034 \r
7035 LRESULT CALLBACK\r
7036 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7037 {\r
7038   static HANDLE hwndText = NULL;\r
7039   int len, newSizeX, newSizeY, flags;\r
7040   static int sizeX, sizeY;\r
7041   char *str;\r
7042   RECT rect;\r
7043   MINMAXINFO *mmi;\r
7044 \r
7045   switch (message) {\r
7046   case WM_INITDIALOG: /* message: initialize dialog box */\r
7047     /* Initialize the dialog items */\r
7048     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7049     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7050     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7051     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7052     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7053     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7054     SetWindowText(hDlg, commentTitle);\r
7055     if (editComment) {\r
7056       SetFocus(hwndText);\r
7057     } else {\r
7058       SetFocus(GetDlgItem(hDlg, IDOK));\r
7059     }\r
7060     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7061                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7062                 MAKELPARAM(FALSE, 0));\r
7063     /* Size and position the dialog */\r
7064     if (!commentDialog) {\r
7065       commentDialog = hDlg;\r
7066       flags = SWP_NOZORDER;\r
7067       GetClientRect(hDlg, &rect);\r
7068       sizeX = rect.right;\r
7069       sizeY = rect.bottom;\r
7070       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7071           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7072         WINDOWPLACEMENT wp;\r
7073         EnsureOnScreen(&commentX, &commentY);\r
7074         wp.length = sizeof(WINDOWPLACEMENT);\r
7075         wp.flags = 0;\r
7076         wp.showCmd = SW_SHOW;\r
7077         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7078         wp.rcNormalPosition.left = commentX;\r
7079         wp.rcNormalPosition.right = commentX + commentW;\r
7080         wp.rcNormalPosition.top = commentY;\r
7081         wp.rcNormalPosition.bottom = commentY + commentH;\r
7082         SetWindowPlacement(hDlg, &wp);\r
7083 \r
7084         GetClientRect(hDlg, &rect);\r
7085         newSizeX = rect.right;\r
7086         newSizeY = rect.bottom;\r
7087         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7088                               newSizeX, newSizeY);\r
7089         sizeX = newSizeX;\r
7090         sizeY = newSizeY;\r
7091       }\r
7092     }\r
7093     return FALSE;\r
7094 \r
7095   case WM_COMMAND: /* message: received a command */\r
7096     switch (LOWORD(wParam)) {\r
7097     case IDOK:\r
7098       if (editComment) {\r
7099         char *p, *q;\r
7100         /* Read changed options from the dialog box */\r
7101         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7102         len = GetWindowTextLength(hwndText);\r
7103         str = (char *) malloc(len + 1);\r
7104         GetWindowText(hwndText, str, len + 1);\r
7105         p = q = str;\r
7106         while (*q) {\r
7107           if (*q == '\r')\r
7108             q++;\r
7109           else\r
7110             *p++ = *q++;\r
7111         }\r
7112         *p = NULLCHAR;\r
7113         ReplaceComment(commentIndex, str);\r
7114         free(str);\r
7115       }\r
7116       CommentPopDown();\r
7117       return TRUE;\r
7118 \r
7119     case IDCANCEL:\r
7120     case OPT_CancelComment:\r
7121       CommentPopDown();\r
7122       return TRUE;\r
7123 \r
7124     case OPT_ClearComment:\r
7125       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7126       break;\r
7127 \r
7128     case OPT_EditComment:\r
7129       EditCommentEvent();\r
7130       return TRUE;\r
7131 \r
7132     default:\r
7133       break;\r
7134     }\r
7135     break;\r
7136 \r
7137   case WM_SIZE:\r
7138     newSizeX = LOWORD(lParam);\r
7139     newSizeY = HIWORD(lParam);\r
7140     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7141     sizeX = newSizeX;\r
7142     sizeY = newSizeY;\r
7143     break;\r
7144 \r
7145   case WM_GETMINMAXINFO:\r
7146     /* Prevent resizing window too small */\r
7147     mmi = (MINMAXINFO *) lParam;\r
7148     mmi->ptMinTrackSize.x = 100;\r
7149     mmi->ptMinTrackSize.y = 100;\r
7150     break;\r
7151   }\r
7152   return FALSE;\r
7153 }\r
7154 \r
7155 VOID\r
7156 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7157 {\r
7158   FARPROC lpProc;\r
7159   char *p, *q;\r
7160 \r
7161   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7162 \r
7163   if (str == NULL) str = "";\r
7164   p = (char *) malloc(2 * strlen(str) + 2);\r
7165   q = p;\r
7166   while (*str) {\r
7167     if (*str == '\n') *q++ = '\r';\r
7168     *q++ = *str++;\r
7169   }\r
7170   *q = NULLCHAR;\r
7171   if (commentText != NULL) free(commentText);\r
7172 \r
7173   commentIndex = index;\r
7174   commentTitle = title;\r
7175   commentText = p;\r
7176   editComment = edit;\r
7177 \r
7178   if (commentDialog) {\r
7179     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7180     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7181   } else {\r
7182     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7183     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7184                  hwndMain, (DLGPROC)lpProc);\r
7185     FreeProcInstance(lpProc);\r
7186   }\r
7187   commentDialogUp = TRUE;\r
7188 }\r
7189 \r
7190 \r
7191 /*---------------------------------------------------------------------------*\\r
7192  *\r
7193  * Type-in move dialog functions\r
7194  * \r
7195 \*---------------------------------------------------------------------------*/\r
7196 \r
7197 LRESULT CALLBACK\r
7198 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7199 {\r
7200   char move[MSG_SIZ];\r
7201   HWND hInput;\r
7202   ChessMove moveType;\r
7203   int fromX, fromY, toX, toY;\r
7204   char promoChar;\r
7205 \r
7206   switch (message) {\r
7207   case WM_INITDIALOG:\r
7208     move[0] = (char) lParam;\r
7209     move[1] = NULLCHAR;\r
7210     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7211     hInput = GetDlgItem(hDlg, OPT_Move);\r
7212     SetWindowText(hInput, move);\r
7213     SetFocus(hInput);\r
7214     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7215     return FALSE;\r
7216 \r
7217   case WM_COMMAND:\r
7218     switch (LOWORD(wParam)) {\r
7219     case IDOK:\r
7220       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7221         gameMode != Training) {\r
7222         DisplayMoveError("Displayed move is not current");\r
7223       } else {\r
7224         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7225         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7226           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7227           if (gameMode != Training)\r
7228               forwardMostMove = currentMove;\r
7229           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7230         } else {\r
7231           DisplayMoveError("Could not parse move");\r
7232         }\r
7233       }\r
7234       EndDialog(hDlg, TRUE);\r
7235       return TRUE;\r
7236     case IDCANCEL:\r
7237       EndDialog(hDlg, FALSE);\r
7238       return TRUE;\r
7239     default:\r
7240       break;\r
7241     }\r
7242     break;\r
7243   }\r
7244   return FALSE;\r
7245 }\r
7246 \r
7247 VOID\r
7248 PopUpMoveDialog(char firstchar)\r
7249 {\r
7250     FARPROC lpProc;\r
7251     \r
7252     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7253         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7254         gameMode == AnalyzeMode || gameMode == EditGame || \r
7255         gameMode == EditPosition || gameMode == IcsExamining ||\r
7256         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7257         gameMode == Training) {\r
7258       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7259       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7260         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7261       FreeProcInstance(lpProc);\r
7262     }\r
7263 }\r
7264 \r
7265 /*---------------------------------------------------------------------------*\\r
7266  *\r
7267  * Type-in name dialog functions\r
7268  * \r
7269 \*---------------------------------------------------------------------------*/\r
7270 \r
7271 LRESULT CALLBACK\r
7272 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7273 {\r
7274   char move[MSG_SIZ];\r
7275   HWND hInput;\r
7276 \r
7277   switch (message) {\r
7278   case WM_INITDIALOG:\r
7279     move[0] = (char) lParam;\r
7280     move[1] = NULLCHAR;\r
7281     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7282     hInput = GetDlgItem(hDlg, OPT_Name);\r
7283     SetWindowText(hInput, move);\r
7284     SetFocus(hInput);\r
7285     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7286     return FALSE;\r
7287 \r
7288   case WM_COMMAND:\r
7289     switch (LOWORD(wParam)) {\r
7290     case IDOK:\r
7291       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7292       appData.userName = strdup(move);\r
7293 \r
7294       EndDialog(hDlg, TRUE);\r
7295       return TRUE;\r
7296     case IDCANCEL:\r
7297       EndDialog(hDlg, FALSE);\r
7298       return TRUE;\r
7299     default:\r
7300       break;\r
7301     }\r
7302     break;\r
7303   }\r
7304   return FALSE;\r
7305 }\r
7306 \r
7307 VOID\r
7308 PopUpNameDialog(char firstchar)\r
7309 {\r
7310     FARPROC lpProc;\r
7311     \r
7312       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7313       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7314         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7315       FreeProcInstance(lpProc);\r
7316 }\r
7317 \r
7318 /*---------------------------------------------------------------------------*\\r
7319  *\r
7320  *  Error dialogs\r
7321  * \r
7322 \*---------------------------------------------------------------------------*/\r
7323 \r
7324 /* Nonmodal error box */\r
7325 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7326                              WPARAM wParam, LPARAM lParam);\r
7327 \r
7328 VOID\r
7329 ErrorPopUp(char *title, char *content)\r
7330 {\r
7331   FARPROC lpProc;\r
7332   char *p, *q;\r
7333   BOOLEAN modal = hwndMain == NULL;\r
7334 \r
7335   p = content;\r
7336   q = errorMessage;\r
7337   while (*p) {\r
7338     if (*p == '\n') {\r
7339       if (modal) {\r
7340         *q++ = ' ';\r
7341         p++;\r
7342       } else {\r
7343         *q++ = '\r';\r
7344         *q++ = *p++;\r
7345       }\r
7346     } else {\r
7347       *q++ = *p++;\r
7348     }\r
7349   }\r
7350   *q = NULLCHAR;\r
7351   strncpy(errorTitle, title, sizeof(errorTitle));\r
7352   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7353   \r
7354   if (modal) {\r
7355     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7356   } else {\r
7357     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7358     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7359                  hwndMain, (DLGPROC)lpProc);\r
7360     FreeProcInstance(lpProc);\r
7361   }\r
7362 }\r
7363 \r
7364 VOID\r
7365 ErrorPopDown()\r
7366 {\r
7367   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7368   if (errorDialog == NULL) return;\r
7369   DestroyWindow(errorDialog);\r
7370   errorDialog = NULL;\r
7371 }\r
7372 \r
7373 LRESULT CALLBACK\r
7374 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7375 {\r
7376   HANDLE hwndText;\r
7377   RECT rChild;\r
7378 \r
7379   switch (message) {\r
7380   case WM_INITDIALOG:\r
7381     GetWindowRect(hDlg, &rChild);\r
7382 \r
7383     /*\r
7384     SetWindowPos(hDlg, NULL, rChild.left,\r
7385       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7386       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7387     */\r
7388 \r
7389     /* \r
7390         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7391         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7392         and it doesn't work when you resize the dialog.\r
7393         For now, just give it a default position.\r
7394     */\r
7395     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7396 \r
7397     errorDialog = hDlg;\r
7398     SetWindowText(hDlg, errorTitle);\r
7399     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7400     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7401     return FALSE;\r
7402 \r
7403   case WM_COMMAND:\r
7404     switch (LOWORD(wParam)) {\r
7405     case IDOK:\r
7406     case IDCANCEL:\r
7407       if (errorDialog == hDlg) errorDialog = NULL;\r
7408       DestroyWindow(hDlg);\r
7409       return TRUE;\r
7410 \r
7411     default:\r
7412       break;\r
7413     }\r
7414     break;\r
7415   }\r
7416   return FALSE;\r
7417 }\r
7418 \r
7419 #ifdef GOTHIC\r
7420 HWND gothicDialog = NULL;\r
7421 \r
7422 LRESULT CALLBACK\r
7423 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7424 {\r
7425   HANDLE hwndText;\r
7426   RECT rChild;\r
7427   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7428 \r
7429   switch (message) {\r
7430   case WM_INITDIALOG:\r
7431     GetWindowRect(hDlg, &rChild);\r
7432 \r
7433     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7434                                                              SWP_NOZORDER);\r
7435 \r
7436     /* \r
7437         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7438         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7439         and it doesn't work when you resize the dialog.\r
7440         For now, just give it a default position.\r
7441     */\r
7442     gothicDialog = hDlg;\r
7443     SetWindowText(hDlg, errorTitle);\r
7444     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7445     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7446     return FALSE;\r
7447 \r
7448   case WM_COMMAND:\r
7449     switch (LOWORD(wParam)) {\r
7450     case IDOK:\r
7451     case IDCANCEL:\r
7452       if (errorDialog == hDlg) errorDialog = NULL;\r
7453       DestroyWindow(hDlg);\r
7454       return TRUE;\r
7455 \r
7456     default:\r
7457       break;\r
7458     }\r
7459     break;\r
7460   }\r
7461   return FALSE;\r
7462 }\r
7463 \r
7464 VOID\r
7465 GothicPopUp(char *title, VariantClass variant)\r
7466 {\r
7467   FARPROC lpProc;\r
7468   static char *lastTitle;\r
7469 \r
7470   strncpy(errorTitle, title, sizeof(errorTitle));\r
7471   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7472 \r
7473   if(lastTitle != title && gothicDialog != NULL) {\r
7474     DestroyWindow(gothicDialog);\r
7475     gothicDialog = NULL;\r
7476   }\r
7477   if(variant != VariantNormal && gothicDialog == NULL) {\r
7478     title = lastTitle;\r
7479     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7480     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7481                  hwndMain, (DLGPROC)lpProc);\r
7482     FreeProcInstance(lpProc);\r
7483   }\r
7484 }\r
7485 #endif\r
7486 \r
7487 /*---------------------------------------------------------------------------*\\r
7488  *\r
7489  *  Ics Interaction console functions\r
7490  *\r
7491 \*---------------------------------------------------------------------------*/\r
7492 \r
7493 #define HISTORY_SIZE 64\r
7494 static char *history[HISTORY_SIZE];\r
7495 int histIn = 0, histP = 0;\r
7496 \r
7497 VOID\r
7498 SaveInHistory(char *cmd)\r
7499 {\r
7500   if (history[histIn] != NULL) {\r
7501     free(history[histIn]);\r
7502     history[histIn] = NULL;\r
7503   }\r
7504   if (*cmd == NULLCHAR) return;\r
7505   history[histIn] = StrSave(cmd);\r
7506   histIn = (histIn + 1) % HISTORY_SIZE;\r
7507   if (history[histIn] != NULL) {\r
7508     free(history[histIn]);\r
7509     history[histIn] = NULL;\r
7510   }\r
7511   histP = histIn;\r
7512 }\r
7513 \r
7514 char *\r
7515 PrevInHistory(char *cmd)\r
7516 {\r
7517   int newhp;\r
7518   if (histP == histIn) {\r
7519     if (history[histIn] != NULL) free(history[histIn]);\r
7520     history[histIn] = StrSave(cmd);\r
7521   }\r
7522   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7523   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7524   histP = newhp;\r
7525   return history[histP];\r
7526 }\r
7527 \r
7528 char *\r
7529 NextInHistory()\r
7530 {\r
7531   if (histP == histIn) return NULL;\r
7532   histP = (histP + 1) % HISTORY_SIZE;\r
7533   return history[histP];\r
7534 }\r
7535 \r
7536 typedef struct {\r
7537   char *item;\r
7538   char *command;\r
7539   BOOLEAN getname;\r
7540   BOOLEAN immediate;\r
7541 } IcsTextMenuEntry;\r
7542 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7543 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7544 \r
7545 void\r
7546 ParseIcsTextMenu(char *icsTextMenuString)\r
7547 {\r
7548 //  int flags = 0;\r
7549   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7550   char *p = icsTextMenuString;\r
7551   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7552     free(e->item);\r
7553     e->item = NULL;\r
7554     if (e->command != NULL) {\r
7555       free(e->command);\r
7556       e->command = NULL;\r
7557     }\r
7558     e++;\r
7559   }\r
7560   e = icsTextMenuEntry;\r
7561   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7562     if (*p == ';' || *p == '\n') {\r
7563       e->item = strdup("-");\r
7564       e->command = NULL;\r
7565       p++;\r
7566     } else if (*p == '-') {\r
7567       e->item = strdup("-");\r
7568       e->command = NULL;\r
7569       p++;\r
7570       if (*p) p++;\r
7571     } else {\r
7572       char *q, *r, *s, *t;\r
7573       char c;\r
7574       q = strchr(p, ',');\r
7575       if (q == NULL) break;\r
7576       *q = NULLCHAR;\r
7577       r = strchr(q + 1, ',');\r
7578       if (r == NULL) break;\r
7579       *r = NULLCHAR;\r
7580       s = strchr(r + 1, ',');\r
7581       if (s == NULL) break;\r
7582       *s = NULLCHAR;\r
7583       c = ';';\r
7584       t = strchr(s + 1, c);\r
7585       if (t == NULL) {\r
7586         c = '\n';\r
7587         t = strchr(s + 1, c);\r
7588       }\r
7589       if (t != NULL) *t = NULLCHAR;\r
7590       e->item = strdup(p);\r
7591       e->command = strdup(q + 1);\r
7592       e->getname = *(r + 1) != '0';\r
7593       e->immediate = *(s + 1) != '0';\r
7594       *q = ',';\r
7595       *r = ',';\r
7596       *s = ',';\r
7597       if (t == NULL) break;\r
7598       *t = c;\r
7599       p = t + 1;\r
7600     }\r
7601     e++;\r
7602   } \r
7603 }\r
7604 \r
7605 HMENU\r
7606 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7607 {\r
7608   HMENU hmenu, h;\r
7609   int i = 0;\r
7610   hmenu = LoadMenu(hInst, "TextMenu");\r
7611   h = GetSubMenu(hmenu, 0);\r
7612   while (e->item) {\r
7613     if (strcmp(e->item, "-") == 0) {\r
7614       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7615     } else {\r
7616       if (e->item[0] == '|') {\r
7617         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7618                    IDM_CommandX + i, &e->item[1]);\r
7619       } else {\r
7620         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7621       }\r
7622     }\r
7623     e++;\r
7624     i++;\r
7625   } \r
7626   return hmenu;\r
7627 }\r
7628 \r
7629 WNDPROC consoleTextWindowProc;\r
7630 \r
7631 void\r
7632 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7633 {\r
7634   char buf[MSG_SIZ], name[MSG_SIZ];\r
7635   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7636   CHARRANGE sel;\r
7637 \r
7638   if (!getname) {\r
7639     SetWindowText(hInput, command);\r
7640     if (immediate) {\r
7641       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7642     } else {\r
7643       sel.cpMin = 999999;\r
7644       sel.cpMax = 999999;\r
7645       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7646       SetFocus(hInput);\r
7647     }\r
7648     return;\r
7649   }    \r
7650   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7651   if (sel.cpMin == sel.cpMax) {\r
7652     /* Expand to surrounding word */\r
7653     TEXTRANGE tr;\r
7654     do {\r
7655       tr.chrg.cpMax = sel.cpMin;\r
7656       tr.chrg.cpMin = --sel.cpMin;\r
7657       if (sel.cpMin < 0) break;\r
7658       tr.lpstrText = name;\r
7659       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7660     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7661     sel.cpMin++;\r
7662 \r
7663     do {\r
7664       tr.chrg.cpMin = sel.cpMax;\r
7665       tr.chrg.cpMax = ++sel.cpMax;\r
7666       tr.lpstrText = name;\r
7667       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7668     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7669     sel.cpMax--;\r
7670 \r
7671     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7672       MessageBeep(MB_ICONEXCLAMATION);\r
7673       return;\r
7674     }\r
7675     tr.chrg = sel;\r
7676     tr.lpstrText = name;\r
7677     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7678   } else {\r
7679     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7680       MessageBeep(MB_ICONEXCLAMATION);\r
7681       return;\r
7682     }\r
7683     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7684   }\r
7685   if (immediate) {\r
7686     sprintf(buf, "%s %s", command, name);\r
7687     SetWindowText(hInput, buf);\r
7688     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7689   } else {\r
7690     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7691     SetWindowText(hInput, buf);\r
7692     sel.cpMin = 999999;\r
7693     sel.cpMax = 999999;\r
7694     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7695     SetFocus(hInput);\r
7696   }\r
7697 }\r
7698 \r
7699 LRESULT CALLBACK \r
7700 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7701 {\r
7702   HWND hInput;\r
7703   CHARRANGE sel;\r
7704 \r
7705   switch (message) {\r
7706   case WM_KEYDOWN:\r
7707     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7708     switch (wParam) {\r
7709     case VK_PRIOR:\r
7710       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7711       return 0;\r
7712     case VK_NEXT:\r
7713       sel.cpMin = 999999;\r
7714       sel.cpMax = 999999;\r
7715       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7716       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7717       return 0;\r
7718     }\r
7719     break;\r
7720   case WM_CHAR:\r
7721     if (wParam == '\t') {\r
7722       if (GetKeyState(VK_SHIFT) < 0) {\r
7723         /* shifted */\r
7724         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7725         if (buttonDesc[0].hwnd) {\r
7726           SetFocus(buttonDesc[0].hwnd);\r
7727         } else {\r
7728           SetFocus(hwndMain);\r
7729         }\r
7730       } else {\r
7731         /* unshifted */\r
7732         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7733       }\r
7734     } else {\r
7735       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7736       SetFocus(hInput);\r
7737       SendMessage(hInput, message, wParam, lParam);\r
7738     }\r
7739     return 0;\r
7740   case WM_PASTE:\r
7741     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7742     SetFocus(hInput);\r
7743     return SendMessage(hInput, message, wParam, lParam);\r
7744   case WM_MBUTTONDOWN:\r
7745     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7746   case WM_RBUTTONDOWN:\r
7747     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7748       /* Move selection here if it was empty */\r
7749       POINT pt;\r
7750       pt.x = LOWORD(lParam);\r
7751       pt.y = HIWORD(lParam);\r
7752       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7753       if (sel.cpMin == sel.cpMax) {\r
7754         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7755         sel.cpMax = sel.cpMin;\r
7756         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7757       }\r
7758       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7759     }\r
7760     return 0;\r
7761   case WM_RBUTTONUP:\r
7762     if (GetKeyState(VK_SHIFT) & ~1) {\r
7763       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7764         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7765     } else {\r
7766       POINT pt;\r
7767       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7768       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7769       if (sel.cpMin == sel.cpMax) {\r
7770         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7771         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7772       }\r
7773       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7774         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7775       }\r
7776       pt.x = LOWORD(lParam);\r
7777       pt.y = HIWORD(lParam);\r
7778       MenuPopup(hwnd, pt, hmenu, -1);\r
7779     }\r
7780     return 0;\r
7781   case WM_COMMAND:\r
7782     switch (LOWORD(wParam)) {\r
7783     case IDM_QuickPaste:\r
7784       {\r
7785         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7786         if (sel.cpMin == sel.cpMax) {\r
7787           MessageBeep(MB_ICONEXCLAMATION);\r
7788           return 0;\r
7789         }\r
7790         SendMessage(hwnd, WM_COPY, 0, 0);\r
7791         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7792         SendMessage(hInput, WM_PASTE, 0, 0);\r
7793         SetFocus(hInput);\r
7794         return 0;\r
7795       }\r
7796     case IDM_Cut:\r
7797       SendMessage(hwnd, WM_CUT, 0, 0);\r
7798       return 0;\r
7799     case IDM_Paste:\r
7800       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7801       return 0;\r
7802     case IDM_Copy:\r
7803       SendMessage(hwnd, WM_COPY, 0, 0);\r
7804       return 0;\r
7805     default:\r
7806       {\r
7807         int i = LOWORD(wParam) - IDM_CommandX;\r
7808         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7809             icsTextMenuEntry[i].command != NULL) {\r
7810           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7811                    icsTextMenuEntry[i].getname,\r
7812                    icsTextMenuEntry[i].immediate);\r
7813           return 0;\r
7814         }\r
7815       }\r
7816       break;\r
7817     }\r
7818     break;\r
7819   }\r
7820   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7821 }\r
7822 \r
7823 WNDPROC consoleInputWindowProc;\r
7824 \r
7825 LRESULT CALLBACK\r
7826 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7827 {\r
7828   char buf[MSG_SIZ];\r
7829   char *p;\r
7830   static BOOL sendNextChar = FALSE;\r
7831   static BOOL quoteNextChar = FALSE;\r
7832   InputSource *is = consoleInputSource;\r
7833   CHARFORMAT cf;\r
7834   CHARRANGE sel;\r
7835 \r
7836   switch (message) {\r
7837   case WM_CHAR:\r
7838     if (!appData.localLineEditing || sendNextChar) {\r
7839       is->buf[0] = (CHAR) wParam;\r
7840       is->count = 1;\r
7841       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7842       sendNextChar = FALSE;\r
7843       return 0;\r
7844     }\r
7845     if (quoteNextChar) {\r
7846       buf[0] = (char) wParam;\r
7847       buf[1] = NULLCHAR;\r
7848       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7849       quoteNextChar = FALSE;\r
7850       return 0;\r
7851     }\r
7852     switch (wParam) {\r
7853     case '\r':   /* Enter key */\r
7854       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7855       if (consoleEcho) SaveInHistory(is->buf);\r
7856       is->buf[is->count++] = '\n';\r
7857       is->buf[is->count] = NULLCHAR;\r
7858       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7859       if (consoleEcho) {\r
7860         ConsoleOutput(is->buf, is->count, TRUE);\r
7861       } else if (appData.localLineEditing) {\r
7862         ConsoleOutput("\n", 1, TRUE);\r
7863       }\r
7864       /* fall thru */\r
7865     case '\033': /* Escape key */\r
7866       SetWindowText(hwnd, "");\r
7867       cf.cbSize = sizeof(CHARFORMAT);\r
7868       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7869       if (consoleEcho) {\r
7870         cf.crTextColor = textAttribs[ColorNormal].color;\r
7871       } else {\r
7872         cf.crTextColor = COLOR_ECHOOFF;\r
7873       }\r
7874       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7875       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7876       return 0;\r
7877     case '\t':   /* Tab key */\r
7878       if (GetKeyState(VK_SHIFT) < 0) {\r
7879         /* shifted */\r
7880         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7881       } else {\r
7882         /* unshifted */\r
7883         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7884         if (buttonDesc[0].hwnd) {\r
7885           SetFocus(buttonDesc[0].hwnd);\r
7886         } else {\r
7887           SetFocus(hwndMain);\r
7888         }\r
7889       }\r
7890       return 0;\r
7891     case '\023': /* Ctrl+S */\r
7892       sendNextChar = TRUE;\r
7893       return 0;\r
7894     case '\021': /* Ctrl+Q */\r
7895       quoteNextChar = TRUE;\r
7896       return 0;\r
7897     default:\r
7898       break;\r
7899     }\r
7900     break;\r
7901   case WM_KEYDOWN:\r
7902     switch (wParam) {\r
7903     case VK_UP:\r
7904       GetWindowText(hwnd, buf, MSG_SIZ);\r
7905       p = PrevInHistory(buf);\r
7906       if (p != NULL) {\r
7907         SetWindowText(hwnd, p);\r
7908         sel.cpMin = 999999;\r
7909         sel.cpMax = 999999;\r
7910         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7911         return 0;\r
7912       }\r
7913       break;\r
7914     case VK_DOWN:\r
7915       p = NextInHistory();\r
7916       if (p != NULL) {\r
7917         SetWindowText(hwnd, p);\r
7918         sel.cpMin = 999999;\r
7919         sel.cpMax = 999999;\r
7920         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7921         return 0;\r
7922       }\r
7923       break;\r
7924     case VK_HOME:\r
7925     case VK_END:\r
7926       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7927       /* fall thru */\r
7928     case VK_PRIOR:\r
7929     case VK_NEXT:\r
7930       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7931       return 0;\r
7932     }\r
7933     break;\r
7934   case WM_MBUTTONDOWN:\r
7935     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7936       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7937     break;\r
7938   case WM_RBUTTONUP:\r
7939     if (GetKeyState(VK_SHIFT) & ~1) {\r
7940       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7941         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7942     } else {\r
7943       POINT pt;\r
7944       HMENU hmenu;\r
7945       hmenu = LoadMenu(hInst, "InputMenu");\r
7946       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7947       if (sel.cpMin == sel.cpMax) {\r
7948         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7949         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7950       }\r
7951       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7952         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7953       }\r
7954       pt.x = LOWORD(lParam);\r
7955       pt.y = HIWORD(lParam);\r
7956       MenuPopup(hwnd, pt, hmenu, -1);\r
7957     }\r
7958     return 0;\r
7959   case WM_COMMAND:\r
7960     switch (LOWORD(wParam)) { \r
7961     case IDM_Undo:\r
7962       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7963       return 0;\r
7964     case IDM_SelectAll:\r
7965       sel.cpMin = 0;\r
7966       sel.cpMax = -1; /*999999?*/\r
7967       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7968       return 0;\r
7969     case IDM_Cut:\r
7970       SendMessage(hwnd, WM_CUT, 0, 0);\r
7971       return 0;\r
7972     case IDM_Paste:\r
7973       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7974       return 0;\r
7975     case IDM_Copy:\r
7976       SendMessage(hwnd, WM_COPY, 0, 0);\r
7977       return 0;\r
7978     }\r
7979     break;\r
7980   }\r
7981   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7982 }\r
7983 \r
7984 #define CO_MAX  100000\r
7985 #define CO_TRIM   1000\r
7986 \r
7987 LRESULT CALLBACK\r
7988 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7989 {\r
7990   static SnapData sd;\r
7991   static HWND hText, hInput /*, hFocus*/;\r
7992 //  InputSource *is = consoleInputSource;\r
7993   RECT rect;\r
7994   static int sizeX, sizeY;\r
7995   int newSizeX, newSizeY;\r
7996   MINMAXINFO *mmi;\r
7997 \r
7998   switch (message) {\r
7999   case WM_INITDIALOG: /* message: initialize dialog box */\r
8000     hwndConsole = hDlg;\r
8001     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8002     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8003     SetFocus(hInput);\r
8004     consoleTextWindowProc = (WNDPROC)\r
8005       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8006     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8007     consoleInputWindowProc = (WNDPROC)\r
8008       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8009     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8010     Colorize(ColorNormal, TRUE);\r
8011     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8012     ChangedConsoleFont();\r
8013     GetClientRect(hDlg, &rect);\r
8014     sizeX = rect.right;\r
8015     sizeY = rect.bottom;\r
8016     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8017         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8018       WINDOWPLACEMENT wp;\r
8019       EnsureOnScreen(&consoleX, &consoleY);\r
8020       wp.length = sizeof(WINDOWPLACEMENT);\r
8021       wp.flags = 0;\r
8022       wp.showCmd = SW_SHOW;\r
8023       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8024       wp.rcNormalPosition.left = consoleX;\r
8025       wp.rcNormalPosition.right = consoleX + consoleW;\r
8026       wp.rcNormalPosition.top = consoleY;\r
8027       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8028       SetWindowPlacement(hDlg, &wp);\r
8029     }\r
8030 #if 0 \r
8031    // [HGM] Chessknight's change 2004-07-13\r
8032    else { /* Determine Defaults */\r
8033        WINDOWPLACEMENT wp;\r
8034        consoleX = winWidth + 1;\r
8035        consoleY = boardY;\r
8036        consoleW = screenWidth -  winWidth;\r
8037        consoleH = winHeight;\r
8038        EnsureOnScreen(&consoleX, &consoleY);\r
8039        wp.length = sizeof(WINDOWPLACEMENT);\r
8040        wp.flags = 0;\r
8041        wp.showCmd = SW_SHOW;\r
8042        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8043        wp.rcNormalPosition.left = consoleX;\r
8044        wp.rcNormalPosition.right = consoleX + consoleW;\r
8045        wp.rcNormalPosition.top = consoleY;\r
8046        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8047        SetWindowPlacement(hDlg, &wp);\r
8048     }\r
8049 #endif\r
8050     return FALSE;\r
8051 \r
8052   case WM_SETFOCUS:\r
8053     SetFocus(hInput);\r
8054     return 0;\r
8055 \r
8056   case WM_CLOSE:\r
8057     ExitEvent(0);\r
8058     /* not reached */\r
8059     break;\r
8060 \r
8061   case WM_SIZE:\r
8062     if (IsIconic(hDlg)) break;\r
8063     newSizeX = LOWORD(lParam);\r
8064     newSizeY = HIWORD(lParam);\r
8065     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8066       RECT rectText, rectInput;\r
8067       POINT pt;\r
8068       int newTextHeight, newTextWidth;\r
8069       GetWindowRect(hText, &rectText);\r
8070       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8071       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8072       if (newTextHeight < 0) {\r
8073         newSizeY += -newTextHeight;\r
8074         newTextHeight = 0;\r
8075       }\r
8076       SetWindowPos(hText, NULL, 0, 0,\r
8077         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8078       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8079       pt.x = rectInput.left;\r
8080       pt.y = rectInput.top + newSizeY - sizeY;\r
8081       ScreenToClient(hDlg, &pt);\r
8082       SetWindowPos(hInput, NULL, \r
8083         pt.x, pt.y, /* needs client coords */   \r
8084         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8085         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8086     }\r
8087     sizeX = newSizeX;\r
8088     sizeY = newSizeY;\r
8089     break;\r
8090 \r
8091   case WM_GETMINMAXINFO:\r
8092     /* Prevent resizing window too small */\r
8093     mmi = (MINMAXINFO *) lParam;\r
8094     mmi->ptMinTrackSize.x = 100;\r
8095     mmi->ptMinTrackSize.y = 100;\r
8096     break;\r
8097 \r
8098   /* [AS] Snapping */\r
8099   case WM_ENTERSIZEMOVE:\r
8100     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8101 \r
8102   case WM_SIZING:\r
8103     return OnSizing( &sd, hDlg, wParam, lParam );\r
8104 \r
8105   case WM_MOVING:\r
8106     return OnMoving( &sd, hDlg, wParam, lParam );\r
8107 \r
8108   case WM_EXITSIZEMOVE:\r
8109     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8110   }\r
8111 \r
8112   return DefWindowProc(hDlg, message, wParam, lParam);\r
8113 }\r
8114 \r
8115 \r
8116 VOID\r
8117 ConsoleCreate()\r
8118 {\r
8119   HWND hCons;\r
8120   if (hwndConsole) return;\r
8121   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8122   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8123 }\r
8124 \r
8125 \r
8126 VOID\r
8127 ConsoleOutput(char* data, int length, int forceVisible)\r
8128 {\r
8129   HWND hText;\r
8130   int trim, exlen;\r
8131   char *p, *q;\r
8132   char buf[CO_MAX+1];\r
8133   POINT pEnd;\r
8134   RECT rect;\r
8135   static int delayLF = 0;\r
8136   CHARRANGE savesel, sel;\r
8137 \r
8138   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8139   p = data;\r
8140   q = buf;\r
8141   if (delayLF) {\r
8142     *q++ = '\r';\r
8143     *q++ = '\n';\r
8144     delayLF = 0;\r
8145   }\r
8146   while (length--) {\r
8147     if (*p == '\n') {\r
8148       if (*++p) {\r
8149         *q++ = '\r';\r
8150         *q++ = '\n';\r
8151       } else {\r
8152         delayLF = 1;\r
8153       }\r
8154     } else if (*p == '\007') {\r
8155        MyPlaySound(&sounds[(int)SoundBell]);\r
8156        p++;\r
8157     } else {\r
8158       *q++ = *p++;\r
8159     }\r
8160   }\r
8161   *q = NULLCHAR;\r
8162   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8163   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8164   /* Save current selection */\r
8165   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8166   exlen = GetWindowTextLength(hText);\r
8167   /* Find out whether current end of text is visible */\r
8168   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8169   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8170   /* Trim existing text if it's too long */\r
8171   if (exlen + (q - buf) > CO_MAX) {\r
8172     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8173     sel.cpMin = 0;\r
8174     sel.cpMax = trim;\r
8175     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8176     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8177     exlen -= trim;\r
8178     savesel.cpMin -= trim;\r
8179     savesel.cpMax -= trim;\r
8180     if (exlen < 0) exlen = 0;\r
8181     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8182     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8183   }\r
8184   /* Append the new text */\r
8185   sel.cpMin = exlen;\r
8186   sel.cpMax = exlen;\r
8187   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8188   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8189   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8190   if (forceVisible || exlen == 0 ||\r
8191       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8192        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8193     /* Scroll to make new end of text visible if old end of text\r
8194        was visible or new text is an echo of user typein */\r
8195     sel.cpMin = 9999999;\r
8196     sel.cpMax = 9999999;\r
8197     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8198     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8199     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8200     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8201   }\r
8202   if (savesel.cpMax == exlen || forceVisible) {\r
8203     /* Move insert point to new end of text if it was at the old\r
8204        end of text or if the new text is an echo of user typein */\r
8205     sel.cpMin = 9999999;\r
8206     sel.cpMax = 9999999;\r
8207     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8208   } else {\r
8209     /* Restore previous selection */\r
8210     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8211   }\r
8212   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8213 }\r
8214 \r
8215 /*---------*/\r
8216 \r
8217 \r
8218 void\r
8219 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8220 {\r
8221   char buf[100];\r
8222   char *str;\r
8223   COLORREF oldFg, oldBg;\r
8224   HFONT oldFont;\r
8225   RECT rect;\r
8226 \r
8227   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8228 \r
8229   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8230   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8231   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8232 \r
8233   rect.left = x;\r
8234   rect.right = x + squareSize;\r
8235   rect.top  = y;\r
8236   rect.bottom = y + squareSize;\r
8237   str = buf;\r
8238 \r
8239   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8240                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8241              y, ETO_CLIPPED|ETO_OPAQUE,\r
8242              &rect, str, strlen(str), NULL);\r
8243 \r
8244   (void) SetTextColor(hdc, oldFg);\r
8245   (void) SetBkColor(hdc, oldBg);\r
8246   (void) SelectObject(hdc, oldFont);\r
8247 }\r
8248 \r
8249 void\r
8250 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8251               RECT *rect, char *color, char *flagFell)\r
8252 {\r
8253   char buf[100];\r
8254   char *str;\r
8255   COLORREF oldFg, oldBg;\r
8256   HFONT oldFont;\r
8257 \r
8258   if (appData.clockMode) {\r
8259     if (tinyLayout)\r
8260       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8261     else\r
8262       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8263     str = buf;\r
8264   } else {\r
8265     str = color;\r
8266   }\r
8267 \r
8268   if (highlight) {\r
8269     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8270     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8271   } else {\r
8272     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8273     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8274   }\r
8275   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8276 \r
8277   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8278              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8279              rect, str, strlen(str), NULL);\r
8280   if(logoHeight > 0 && appData.clockMode) {\r
8281       RECT r;\r
8282       sprintf(buf, "%s %s", TimeString(timeRemaining), flagFell);\r
8283       r.top = rect->top + logoHeight/2;\r
8284       r.left = rect->left;\r
8285       r.right = rect->right;\r
8286       r.bottom = rect->bottom;\r
8287       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8288                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8289                  &r, str, strlen(str), NULL);\r
8290   }\r
8291   (void) SetTextColor(hdc, oldFg);\r
8292   (void) SetBkColor(hdc, oldBg);\r
8293   (void) SelectObject(hdc, oldFont);\r
8294 }\r
8295 \r
8296 \r
8297 int\r
8298 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8299            OVERLAPPED *ovl)\r
8300 {\r
8301   int ok, err;\r
8302 \r
8303   /* [AS]  */\r
8304   if( count <= 0 ) {\r
8305     if (appData.debugMode) {\r
8306       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8307     }\r
8308 \r
8309     return ERROR_INVALID_USER_BUFFER;\r
8310   }\r
8311 \r
8312   ResetEvent(ovl->hEvent);\r
8313   ovl->Offset = ovl->OffsetHigh = 0;\r
8314   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8315   if (ok) {\r
8316     err = NO_ERROR;\r
8317   } else {\r
8318     err = GetLastError();\r
8319     if (err == ERROR_IO_PENDING) {\r
8320       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8321       if (ok)\r
8322         err = NO_ERROR;\r
8323       else\r
8324         err = GetLastError();\r
8325     }\r
8326   }\r
8327   return err;\r
8328 }\r
8329 \r
8330 int\r
8331 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8332             OVERLAPPED *ovl)\r
8333 {\r
8334   int ok, err;\r
8335 \r
8336   ResetEvent(ovl->hEvent);\r
8337   ovl->Offset = ovl->OffsetHigh = 0;\r
8338   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8339   if (ok) {\r
8340     err = NO_ERROR;\r
8341   } else {\r
8342     err = GetLastError();\r
8343     if (err == ERROR_IO_PENDING) {\r
8344       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8345       if (ok)\r
8346         err = NO_ERROR;\r
8347       else\r
8348         err = GetLastError();\r
8349     }\r
8350   }\r
8351   return err;\r
8352 }\r
8353 \r
8354 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8355 void CheckForInputBufferFull( InputSource * is )\r
8356 {\r
8357     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8358         /* Look for end of line */\r
8359         char * p = is->buf;\r
8360         \r
8361         while( p < is->next && *p != '\n' ) {\r
8362             p++;\r
8363         }\r
8364 \r
8365         if( p >= is->next ) {\r
8366             if (appData.debugMode) {\r
8367                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8368             }\r
8369 \r
8370             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8371             is->count = (DWORD) -1;\r
8372             is->next = is->buf;\r
8373         }\r
8374     }\r
8375 }\r
8376 \r
8377 DWORD\r
8378 InputThread(LPVOID arg)\r
8379 {\r
8380   InputSource *is;\r
8381   OVERLAPPED ovl;\r
8382 \r
8383   is = (InputSource *) arg;\r
8384   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8385   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8386   while (is->hThread != NULL) {\r
8387     is->error = DoReadFile(is->hFile, is->next,\r
8388                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8389                            &is->count, &ovl);\r
8390     if (is->error == NO_ERROR) {\r
8391       is->next += is->count;\r
8392     } else {\r
8393       if (is->error == ERROR_BROKEN_PIPE) {\r
8394         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8395         is->count = 0;\r
8396       } else {\r
8397         is->count = (DWORD) -1;\r
8398         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8399         break; \r
8400       }\r
8401     }\r
8402 \r
8403     CheckForInputBufferFull( is );\r
8404 \r
8405     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8406 \r
8407     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8408 \r
8409     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8410   }\r
8411 \r
8412   CloseHandle(ovl.hEvent);\r
8413   CloseHandle(is->hFile);\r
8414 \r
8415   if (appData.debugMode) {\r
8416     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8417   }\r
8418 \r
8419   return 0;\r
8420 }\r
8421 \r
8422 \r
8423 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8424 DWORD\r
8425 NonOvlInputThread(LPVOID arg)\r
8426 {\r
8427   InputSource *is;\r
8428   char *p, *q;\r
8429   int i;\r
8430   char prev;\r
8431 \r
8432   is = (InputSource *) arg;\r
8433   while (is->hThread != NULL) {\r
8434     is->error = ReadFile(is->hFile, is->next,\r
8435                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8436                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8437     if (is->error == NO_ERROR) {\r
8438       /* Change CRLF to LF */\r
8439       if (is->next > is->buf) {\r
8440         p = is->next - 1;\r
8441         i = is->count + 1;\r
8442       } else {\r
8443         p = is->next;\r
8444         i = is->count;\r
8445       }\r
8446       q = p;\r
8447       prev = NULLCHAR;\r
8448       while (i > 0) {\r
8449         if (prev == '\r' && *p == '\n') {\r
8450           *(q-1) = '\n';\r
8451           is->count--;\r
8452         } else { \r
8453           *q++ = *p;\r
8454         }\r
8455         prev = *p++;\r
8456         i--;\r
8457       }\r
8458       *q = NULLCHAR;\r
8459       is->next = q;\r
8460     } else {\r
8461       if (is->error == ERROR_BROKEN_PIPE) {\r
8462         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8463         is->count = 0; \r
8464       } else {\r
8465         is->count = (DWORD) -1;\r
8466       }\r
8467     }\r
8468 \r
8469     CheckForInputBufferFull( is );\r
8470 \r
8471     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8472 \r
8473     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8474 \r
8475     if (is->count < 0) break;  /* Quit on error */\r
8476   }\r
8477   CloseHandle(is->hFile);\r
8478   return 0;\r
8479 }\r
8480 \r
8481 DWORD\r
8482 SocketInputThread(LPVOID arg)\r
8483 {\r
8484   InputSource *is;\r
8485 \r
8486   is = (InputSource *) arg;\r
8487   while (is->hThread != NULL) {\r
8488     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8489     if ((int)is->count == SOCKET_ERROR) {\r
8490       is->count = (DWORD) -1;\r
8491       is->error = WSAGetLastError();\r
8492     } else {\r
8493       is->error = NO_ERROR;\r
8494       is->next += is->count;\r
8495       if (is->count == 0 && is->second == is) {\r
8496         /* End of file on stderr; quit with no message */\r
8497         break;\r
8498       }\r
8499     }\r
8500     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8501 \r
8502     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8503 \r
8504     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8505   }\r
8506   return 0;\r
8507 }\r
8508 \r
8509 VOID\r
8510 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8511 {\r
8512   InputSource *is;\r
8513 \r
8514   is = (InputSource *) lParam;\r
8515   if (is->lineByLine) {\r
8516     /* Feed in lines one by one */\r
8517     char *p = is->buf;\r
8518     char *q = p;\r
8519     while (q < is->next) {\r
8520       if (*q++ == '\n') {\r
8521         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8522         p = q;\r
8523       }\r
8524     }\r
8525     \r
8526     /* Move any partial line to the start of the buffer */\r
8527     q = is->buf;\r
8528     while (p < is->next) {\r
8529       *q++ = *p++;\r
8530     }\r
8531     is->next = q;\r
8532 \r
8533     if (is->error != NO_ERROR || is->count == 0) {\r
8534       /* Notify backend of the error.  Note: If there was a partial\r
8535          line at the end, it is not flushed through. */\r
8536       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8537     }\r
8538   } else {\r
8539     /* Feed in the whole chunk of input at once */\r
8540     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8541     is->next = is->buf;\r
8542   }\r
8543 }\r
8544 \r
8545 /*---------------------------------------------------------------------------*\\r
8546  *\r
8547  *  Menu enables. Used when setting various modes.\r
8548  *\r
8549 \*---------------------------------------------------------------------------*/\r
8550 \r
8551 typedef struct {\r
8552   int item;\r
8553   int flags;\r
8554 } Enables;\r
8555 \r
8556 VOID\r
8557 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8558 {\r
8559   while (enab->item > 0) {\r
8560     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8561     enab++;\r
8562   }\r
8563 }\r
8564 \r
8565 Enables gnuEnables[] = {\r
8566   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8576   { -1, -1 }\r
8577 };\r
8578 \r
8579 Enables icsEnables[] = {\r
8580   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8586   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8589   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8592   { -1, -1 }\r
8593 };\r
8594 \r
8595 #ifdef ZIPPY\r
8596 Enables zippyEnables[] = {\r
8597   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8598   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8599   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8600   { -1, -1 }\r
8601 };\r
8602 #endif\r
8603 \r
8604 Enables ncpEnables[] = {\r
8605   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8613   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8614   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8620   { -1, -1 }\r
8621 };\r
8622 \r
8623 Enables trainingOnEnables[] = {\r
8624   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8625   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8628   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8629   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8630   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8631   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8632   { -1, -1 }\r
8633 };\r
8634 \r
8635 Enables trainingOffEnables[] = {\r
8636   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8637   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8640   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8641   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8642   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8643   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8644   { -1, -1 }\r
8645 };\r
8646 \r
8647 /* These modify either ncpEnables or gnuEnables */\r
8648 Enables cmailEnables[] = {\r
8649   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8650   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8651   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8652   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8654   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8656   { -1, -1 }\r
8657 };\r
8658 \r
8659 Enables machineThinkingEnables[] = {\r
8660   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8663   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8664   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8665   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8666   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8667   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8668   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8669   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8670   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8671   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8672   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8673   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8674   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8675   { -1, -1 }\r
8676 };\r
8677 \r
8678 Enables userThinkingEnables[] = {\r
8679   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8680   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8681   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8682   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8683   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8684   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8685   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8686   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8687   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8688   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8689   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8690   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8691   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8692   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8693   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8694   { -1, -1 }\r
8695 };\r
8696 \r
8697 /*---------------------------------------------------------------------------*\\r
8698  *\r
8699  *  Front-end interface functions exported by XBoard.\r
8700  *  Functions appear in same order as prototypes in frontend.h.\r
8701  * \r
8702 \*---------------------------------------------------------------------------*/\r
8703 VOID\r
8704 ModeHighlight()\r
8705 {\r
8706   static UINT prevChecked = 0;\r
8707   static int prevPausing = 0;\r
8708   UINT nowChecked;\r
8709 \r
8710   if (pausing != prevPausing) {\r
8711     prevPausing = pausing;\r
8712     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8713                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8714     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8715   }\r
8716 \r
8717   switch (gameMode) {\r
8718   case BeginningOfGame:\r
8719     if (appData.icsActive)\r
8720       nowChecked = IDM_IcsClient;\r
8721     else if (appData.noChessProgram)\r
8722       nowChecked = IDM_EditGame;\r
8723     else\r
8724       nowChecked = IDM_MachineBlack;\r
8725     break;\r
8726   case MachinePlaysBlack:\r
8727     nowChecked = IDM_MachineBlack;\r
8728     break;\r
8729   case MachinePlaysWhite:\r
8730     nowChecked = IDM_MachineWhite;\r
8731     break;\r
8732   case TwoMachinesPlay:\r
8733     nowChecked = IDM_TwoMachines;\r
8734     break;\r
8735   case AnalyzeMode:\r
8736     nowChecked = IDM_AnalysisMode;\r
8737     break;\r
8738   case AnalyzeFile:\r
8739     nowChecked = IDM_AnalyzeFile;\r
8740     break;\r
8741   case EditGame:\r
8742     nowChecked = IDM_EditGame;\r
8743     break;\r
8744   case PlayFromGameFile:\r
8745     nowChecked = IDM_LoadGame;\r
8746     break;\r
8747   case EditPosition:\r
8748     nowChecked = IDM_EditPosition;\r
8749     break;\r
8750   case Training:\r
8751     nowChecked = IDM_Training;\r
8752     break;\r
8753   case IcsPlayingWhite:\r
8754   case IcsPlayingBlack:\r
8755   case IcsObserving:\r
8756   case IcsIdle:\r
8757     nowChecked = IDM_IcsClient;\r
8758     break;\r
8759   default:\r
8760   case EndOfGame:\r
8761     nowChecked = 0;\r
8762     break;\r
8763   }\r
8764   if (prevChecked != 0)\r
8765     (void) CheckMenuItem(GetMenu(hwndMain),\r
8766                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8767   if (nowChecked != 0)\r
8768     (void) CheckMenuItem(GetMenu(hwndMain),\r
8769                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8770 \r
8771   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8772     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8773                           MF_BYCOMMAND|MF_ENABLED);\r
8774   } else {\r
8775     (void) EnableMenuItem(GetMenu(hwndMain), \r
8776                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8777   }\r
8778 \r
8779   prevChecked = nowChecked;\r
8780 \r
8781   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8782   if (appData.icsActive) {\r
8783        if (appData.icsEngineAnalyze) {\r
8784                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8785                        MF_BYCOMMAND|MF_CHECKED);\r
8786        } else {\r
8787                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8788                        MF_BYCOMMAND|MF_UNCHECKED);\r
8789        }\r
8790   }\r
8791 }\r
8792 \r
8793 VOID\r
8794 SetICSMode()\r
8795 {\r
8796   HMENU hmenu = GetMenu(hwndMain);\r
8797   SetMenuEnables(hmenu, icsEnables);\r
8798   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8799     MF_BYPOSITION|MF_ENABLED);\r
8800 #ifdef ZIPPY\r
8801   if (appData.zippyPlay) {\r
8802     SetMenuEnables(hmenu, zippyEnables);\r
8803     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8804          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8805           MF_BYCOMMAND|MF_ENABLED);\r
8806   }\r
8807 #endif\r
8808 }\r
8809 \r
8810 VOID\r
8811 SetGNUMode()\r
8812 {\r
8813   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8814 }\r
8815 \r
8816 VOID\r
8817 SetNCPMode()\r
8818 {\r
8819   HMENU hmenu = GetMenu(hwndMain);\r
8820   SetMenuEnables(hmenu, ncpEnables);\r
8821   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8822     MF_BYPOSITION|MF_GRAYED);\r
8823     DrawMenuBar(hwndMain);\r
8824 }\r
8825 \r
8826 VOID\r
8827 SetCmailMode()\r
8828 {\r
8829   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8830 }\r
8831 \r
8832 VOID \r
8833 SetTrainingModeOn()\r
8834 {\r
8835   int i;\r
8836   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8837   for (i = 0; i < N_BUTTONS; i++) {\r
8838     if (buttonDesc[i].hwnd != NULL)\r
8839       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8840   }\r
8841   CommentPopDown();\r
8842 }\r
8843 \r
8844 VOID SetTrainingModeOff()\r
8845 {\r
8846   int i;\r
8847   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8848   for (i = 0; i < N_BUTTONS; i++) {\r
8849     if (buttonDesc[i].hwnd != NULL)\r
8850       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8851   }\r
8852 }\r
8853 \r
8854 \r
8855 VOID\r
8856 SetUserThinkingEnables()\r
8857 {\r
8858   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8859 }\r
8860 \r
8861 VOID\r
8862 SetMachineThinkingEnables()\r
8863 {\r
8864   HMENU hMenu = GetMenu(hwndMain);\r
8865   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8866 \r
8867   SetMenuEnables(hMenu, machineThinkingEnables);\r
8868 \r
8869   if (gameMode == MachinePlaysBlack) {\r
8870     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8871   } else if (gameMode == MachinePlaysWhite) {\r
8872     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8873   } else if (gameMode == TwoMachinesPlay) {\r
8874     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8875   }\r
8876 }\r
8877 \r
8878 \r
8879 VOID\r
8880 DisplayTitle(char *str)\r
8881 {\r
8882   char title[MSG_SIZ], *host;\r
8883   if (str[0] != NULLCHAR) {\r
8884     strcpy(title, str);\r
8885   } else if (appData.icsActive) {\r
8886     if (appData.icsCommPort[0] != NULLCHAR)\r
8887       host = "ICS";\r
8888     else \r
8889       host = appData.icsHost;\r
8890     sprintf(title, "%s: %s", szTitle, host);\r
8891   } else if (appData.noChessProgram) {\r
8892     strcpy(title, szTitle);\r
8893   } else {\r
8894     strcpy(title, szTitle);\r
8895     strcat(title, ": ");\r
8896     strcat(title, first.tidy);\r
8897   }\r
8898   SetWindowText(hwndMain, title);\r
8899 }\r
8900 \r
8901 \r
8902 VOID\r
8903 DisplayMessage(char *str1, char *str2)\r
8904 {\r
8905   HDC hdc;\r
8906   HFONT oldFont;\r
8907   int remain = MESSAGE_TEXT_MAX - 1;\r
8908   int len;\r
8909 \r
8910   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8911   messageText[0] = NULLCHAR;\r
8912   if (*str1) {\r
8913     len = strlen(str1);\r
8914     if (len > remain) len = remain;\r
8915     strncpy(messageText, str1, len);\r
8916     messageText[len] = NULLCHAR;\r
8917     remain -= len;\r
8918   }\r
8919   if (*str2 && remain >= 2) {\r
8920     if (*str1) {\r
8921       strcat(messageText, "  ");\r
8922       remain -= 2;\r
8923     }\r
8924     len = strlen(str2);\r
8925     if (len > remain) len = remain;\r
8926     strncat(messageText, str2, len);\r
8927   }\r
8928   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8929 \r
8930   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8931   hdc = GetDC(hwndMain);\r
8932   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8933   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8934              &messageRect, messageText, strlen(messageText), NULL);\r
8935   (void) SelectObject(hdc, oldFont);\r
8936   (void) ReleaseDC(hwndMain, hdc);\r
8937 }\r
8938 \r
8939 VOID\r
8940 DisplayError(char *str, int error)\r
8941 {\r
8942   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8943   int len;\r
8944 \r
8945   if (error == 0) {\r
8946     strcpy(buf, str);\r
8947   } else {\r
8948     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8949                         NULL, error, LANG_NEUTRAL,\r
8950                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8951     if (len > 0) {\r
8952       sprintf(buf, "%s:\n%s", str, buf2);\r
8953     } else {\r
8954       ErrorMap *em = errmap;\r
8955       while (em->err != 0 && em->err != error) em++;\r
8956       if (em->err != 0) {\r
8957         sprintf(buf, "%s:\n%s", str, em->msg);\r
8958       } else {\r
8959         sprintf(buf, "%s:\nError code %d", str, error);\r
8960       }\r
8961     }\r
8962   }\r
8963   \r
8964   ErrorPopUp("Error", buf);\r
8965 }\r
8966 \r
8967 \r
8968 VOID\r
8969 DisplayMoveError(char *str)\r
8970 {\r
8971   fromX = fromY = -1;\r
8972   ClearHighlights();\r
8973   DrawPosition(FALSE, NULL);\r
8974   if (appData.popupMoveErrors) {\r
8975     ErrorPopUp("Error", str);\r
8976   } else {\r
8977     DisplayMessage(str, "");\r
8978     moveErrorMessageUp = TRUE;\r
8979   }\r
8980 }\r
8981 \r
8982 VOID\r
8983 DisplayFatalError(char *str, int error, int exitStatus)\r
8984 {\r
8985   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8986   int len;\r
8987   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8988 \r
8989   if (error != 0) {\r
8990     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8991                         NULL, error, LANG_NEUTRAL,\r
8992                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8993     if (len > 0) {\r
8994       sprintf(buf, "%s:\n%s", str, buf2);\r
8995     } else {\r
8996       ErrorMap *em = errmap;\r
8997       while (em->err != 0 && em->err != error) em++;\r
8998       if (em->err != 0) {\r
8999         sprintf(buf, "%s:\n%s", str, em->msg);\r
9000       } else {\r
9001         sprintf(buf, "%s:\nError code %d", str, error);\r
9002       }\r
9003     }\r
9004     str = buf;\r
9005   }\r
9006   if (appData.debugMode) {\r
9007     fprintf(debugFP, "%s: %s\n", label, str);\r
9008   }\r
9009   if (appData.popupExitMessage) {\r
9010     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9011                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9012   }\r
9013   ExitEvent(exitStatus);\r
9014 }\r
9015 \r
9016 \r
9017 VOID\r
9018 DisplayInformation(char *str)\r
9019 {\r
9020   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9021 }\r
9022 \r
9023 \r
9024 VOID\r
9025 DisplayNote(char *str)\r
9026 {\r
9027   ErrorPopUp("Note", str);\r
9028 }\r
9029 \r
9030 \r
9031 typedef struct {\r
9032   char *title, *question, *replyPrefix;\r
9033   ProcRef pr;\r
9034 } QuestionParams;\r
9035 \r
9036 LRESULT CALLBACK\r
9037 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9038 {\r
9039   static QuestionParams *qp;\r
9040   char reply[MSG_SIZ];\r
9041   int len, err;\r
9042 \r
9043   switch (message) {\r
9044   case WM_INITDIALOG:\r
9045     qp = (QuestionParams *) lParam;\r
9046     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9047     SetWindowText(hDlg, qp->title);\r
9048     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9049     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9050     return FALSE;\r
9051 \r
9052   case WM_COMMAND:\r
9053     switch (LOWORD(wParam)) {\r
9054     case IDOK:\r
9055       strcpy(reply, qp->replyPrefix);\r
9056       if (*reply) strcat(reply, " ");\r
9057       len = strlen(reply);\r
9058       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9059       strcat(reply, "\n");\r
9060       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9061       EndDialog(hDlg, TRUE);\r
9062       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9063       return TRUE;\r
9064     case IDCANCEL:\r
9065       EndDialog(hDlg, FALSE);\r
9066       return TRUE;\r
9067     default:\r
9068       break;\r
9069     }\r
9070     break;\r
9071   }\r
9072   return FALSE;\r
9073 }\r
9074 \r
9075 VOID\r
9076 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9077 {\r
9078     QuestionParams qp;\r
9079     FARPROC lpProc;\r
9080     \r
9081     qp.title = title;\r
9082     qp.question = question;\r
9083     qp.replyPrefix = replyPrefix;\r
9084     qp.pr = pr;\r
9085     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9086     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9087       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9088     FreeProcInstance(lpProc);\r
9089 }\r
9090 \r
9091 /* [AS] Pick FRC position */\r
9092 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9093 {\r
9094     static int * lpIndexFRC;\r
9095     BOOL index_is_ok;\r
9096     char buf[16];\r
9097 \r
9098     switch( message )\r
9099     {\r
9100     case WM_INITDIALOG:\r
9101         lpIndexFRC = (int *) lParam;\r
9102 \r
9103         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9104 \r
9105         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9106         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9107         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9108         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9109 \r
9110         break;\r
9111 \r
9112     case WM_COMMAND:\r
9113         switch( LOWORD(wParam) ) {\r
9114         case IDOK:\r
9115             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9116             EndDialog( hDlg, 0 );\r
9117             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9118             return TRUE;\r
9119         case IDCANCEL:\r
9120             EndDialog( hDlg, 1 );   \r
9121             return TRUE;\r
9122         case IDC_NFG_Edit:\r
9123             if( HIWORD(wParam) == EN_CHANGE ) {\r
9124                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9125 \r
9126                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9127             }\r
9128             return TRUE;\r
9129         case IDC_NFG_Random:\r
9130             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9131             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9132             return TRUE;\r
9133         }\r
9134 \r
9135         break;\r
9136     }\r
9137 \r
9138     return FALSE;\r
9139 }\r
9140 \r
9141 int NewGameFRC()\r
9142 {\r
9143     int result;\r
9144     int index = appData.defaultFrcPosition;\r
9145     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9146 \r
9147     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9148 \r
9149     if( result == 0 ) {\r
9150         appData.defaultFrcPosition = index;\r
9151     }\r
9152 \r
9153     return result;\r
9154 }\r
9155 \r
9156 /* [AS] Game list options */\r
9157 typedef struct {\r
9158     char id;\r
9159     char * name;\r
9160 } GLT_Item;\r
9161 \r
9162 static GLT_Item GLT_ItemInfo[] = {\r
9163     { GLT_EVENT,      "Event" },\r
9164     { GLT_SITE,       "Site" },\r
9165     { GLT_DATE,       "Date" },\r
9166     { GLT_ROUND,      "Round" },\r
9167     { GLT_PLAYERS,    "Players" },\r
9168     { GLT_RESULT,     "Result" },\r
9169     { GLT_WHITE_ELO,  "White Rating" },\r
9170     { GLT_BLACK_ELO,  "Black Rating" },\r
9171     { GLT_TIME_CONTROL,"Time Control" },\r
9172     { GLT_VARIANT,    "Variant" },\r
9173     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9174     { 0, 0 }\r
9175 };\r
9176 \r
9177 const char * GLT_FindItem( char id )\r
9178 {\r
9179     const char * result = 0;\r
9180 \r
9181     GLT_Item * list = GLT_ItemInfo;\r
9182 \r
9183     while( list->id != 0 ) {\r
9184         if( list->id == id ) {\r
9185             result = list->name;\r
9186             break;\r
9187         }\r
9188 \r
9189         list++;\r
9190     }\r
9191 \r
9192     return result;\r
9193 }\r
9194 \r
9195 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9196 {\r
9197     const char * name = GLT_FindItem( id );\r
9198 \r
9199     if( name != 0 ) {\r
9200         if( index >= 0 ) {\r
9201             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9202         }\r
9203         else {\r
9204             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9205         }\r
9206     }\r
9207 }\r
9208 \r
9209 void GLT_TagsToList( HWND hDlg, char * tags )\r
9210 {\r
9211     char * pc = tags;\r
9212 \r
9213     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9214 \r
9215     while( *pc ) {\r
9216         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9217         pc++;\r
9218     }\r
9219 \r
9220     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9221 \r
9222     pc = GLT_ALL_TAGS;\r
9223 \r
9224     while( *pc ) {\r
9225         if( strchr( tags, *pc ) == 0 ) {\r
9226             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9227         }\r
9228         pc++;\r
9229     }\r
9230 \r
9231     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9232 }\r
9233 \r
9234 char GLT_ListItemToTag( HWND hDlg, int index )\r
9235 {\r
9236     char result = '\0';\r
9237     char name[128];\r
9238 \r
9239     GLT_Item * list = GLT_ItemInfo;\r
9240 \r
9241     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9242         while( list->id != 0 ) {\r
9243             if( strcmp( list->name, name ) == 0 ) {\r
9244                 result = list->id;\r
9245                 break;\r
9246             }\r
9247 \r
9248             list++;\r
9249         }\r
9250     }\r
9251 \r
9252     return result;\r
9253 }\r
9254 \r
9255 void GLT_MoveSelection( HWND hDlg, int delta )\r
9256 {\r
9257     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9258     int idx2 = idx1 + delta;\r
9259     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9260 \r
9261     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9262         char buf[128];\r
9263 \r
9264         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9265         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9266         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9267         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9268     }\r
9269 }\r
9270 \r
9271 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9272 {\r
9273     static char glt[64];\r
9274     static char * lpUserGLT;\r
9275 \r
9276     switch( message )\r
9277     {\r
9278     case WM_INITDIALOG:\r
9279         lpUserGLT = (char *) lParam;\r
9280         \r
9281         strcpy( glt, lpUserGLT );\r
9282 \r
9283         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9284 \r
9285         /* Initialize list */\r
9286         GLT_TagsToList( hDlg, glt );\r
9287 \r
9288         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9289 \r
9290         break;\r
9291 \r
9292     case WM_COMMAND:\r
9293         switch( LOWORD(wParam) ) {\r
9294         case IDOK:\r
9295             {\r
9296                 char * pc = lpUserGLT;\r
9297                 int idx = 0;\r
9298 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9299                 char id;\r
9300 \r
9301                 do {\r
9302                     id = GLT_ListItemToTag( hDlg, idx );\r
9303 \r
9304                     *pc++ = id;\r
9305                     idx++;\r
9306                 } while( id != '\0' );\r
9307             }\r
9308             EndDialog( hDlg, 0 );\r
9309             return TRUE;\r
9310         case IDCANCEL:\r
9311             EndDialog( hDlg, 1 );\r
9312             return TRUE;\r
9313 \r
9314         case IDC_GLT_Default:\r
9315             strcpy( glt, GLT_DEFAULT_TAGS );\r
9316             GLT_TagsToList( hDlg, glt );\r
9317             return TRUE;\r
9318 \r
9319         case IDC_GLT_Restore:\r
9320             strcpy( glt, lpUserGLT );\r
9321             GLT_TagsToList( hDlg, glt );\r
9322             return TRUE;\r
9323 \r
9324         case IDC_GLT_Up:\r
9325             GLT_MoveSelection( hDlg, -1 );\r
9326             return TRUE;\r
9327 \r
9328         case IDC_GLT_Down:\r
9329             GLT_MoveSelection( hDlg, +1 );\r
9330             return TRUE;\r
9331         }\r
9332 \r
9333         break;\r
9334     }\r
9335 \r
9336     return FALSE;\r
9337 }\r
9338 \r
9339 int GameListOptions()\r
9340 {\r
9341     char glt[64];\r
9342     int result;\r
9343     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9344 \r
9345     strcpy( glt, appData.gameListTags );\r
9346 \r
9347     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9348 \r
9349     if( result == 0 ) {\r
9350         /* [AS] Memory leak here! */\r
9351         appData.gameListTags = strdup( glt ); \r
9352     }\r
9353 \r
9354     return result;\r
9355 }\r
9356 \r
9357 \r
9358 VOID\r
9359 DisplayIcsInteractionTitle(char *str)\r
9360 {\r
9361   char consoleTitle[MSG_SIZ];\r
9362 \r
9363   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9364   SetWindowText(hwndConsole, consoleTitle);\r
9365 }\r
9366 \r
9367 void\r
9368 DrawPosition(int fullRedraw, Board board)\r
9369 {\r
9370   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9371 }\r
9372 \r
9373 \r
9374 VOID\r
9375 ResetFrontEnd()\r
9376 {\r
9377   fromX = fromY = -1;\r
9378   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9379     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9380     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9381     dragInfo.lastpos = dragInfo.pos;\r
9382     dragInfo.start.x = dragInfo.start.y = -1;\r
9383     dragInfo.from = dragInfo.start;\r
9384     ReleaseCapture();\r
9385     DrawPosition(TRUE, NULL);\r
9386   }\r
9387 }\r
9388 \r
9389 \r
9390 VOID\r
9391 CommentPopUp(char *title, char *str)\r
9392 {\r
9393   HWND hwnd = GetActiveWindow();\r
9394   EitherCommentPopUp(0, title, str, FALSE);\r
9395   SetActiveWindow(hwnd);\r
9396 }\r
9397 \r
9398 VOID\r
9399 CommentPopDown(void)\r
9400 {\r
9401   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9402   if (commentDialog) {\r
9403     ShowWindow(commentDialog, SW_HIDE);\r
9404   }\r
9405   commentDialogUp = FALSE;\r
9406 }\r
9407 \r
9408 VOID\r
9409 EditCommentPopUp(int index, char *title, char *str)\r
9410 {\r
9411   EitherCommentPopUp(index, title, str, TRUE);\r
9412 }\r
9413 \r
9414 \r
9415 VOID\r
9416 RingBell()\r
9417 {\r
9418   MyPlaySound(&sounds[(int)SoundMove]);\r
9419 }\r
9420 \r
9421 VOID PlayIcsWinSound()\r
9422 {\r
9423   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9424 }\r
9425 \r
9426 VOID PlayIcsLossSound()\r
9427 {\r
9428   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9429 }\r
9430 \r
9431 VOID PlayIcsDrawSound()\r
9432 {\r
9433   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9434 }\r
9435 \r
9436 VOID PlayIcsUnfinishedSound()\r
9437 {\r
9438   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9439 }\r
9440 \r
9441 VOID\r
9442 PlayAlarmSound()\r
9443 {\r
9444   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9445 }\r
9446 \r
9447 \r
9448 VOID\r
9449 EchoOn()\r
9450 {\r
9451   HWND hInput;\r
9452   consoleEcho = TRUE;\r
9453   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9454   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9455   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9456 }\r
9457 \r
9458 \r
9459 VOID\r
9460 EchoOff()\r
9461 {\r
9462   CHARFORMAT cf;\r
9463   HWND hInput;\r
9464   consoleEcho = FALSE;\r
9465   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9466   /* This works OK: set text and background both to the same color */\r
9467   cf = consoleCF;\r
9468   cf.crTextColor = COLOR_ECHOOFF;\r
9469   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9470   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9471 }\r
9472 \r
9473 /* No Raw()...? */\r
9474 \r
9475 void Colorize(ColorClass cc, int continuation)\r
9476 {\r
9477   currentColorClass = cc;\r
9478   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9479   consoleCF.crTextColor = textAttribs[cc].color;\r
9480   consoleCF.dwEffects = textAttribs[cc].effects;\r
9481   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9482 }\r
9483 \r
9484 char *\r
9485 UserName()\r
9486 {\r
9487   static char buf[MSG_SIZ];\r
9488   DWORD bufsiz = MSG_SIZ;\r
9489 \r
9490   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9491         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9492   }\r
9493   if (!GetUserName(buf, &bufsiz)) {\r
9494     /*DisplayError("Error getting user name", GetLastError());*/\r
9495     strcpy(buf, "User");\r
9496   }\r
9497   return buf;\r
9498 }\r
9499 \r
9500 char *\r
9501 HostName()\r
9502 {\r
9503   static char buf[MSG_SIZ];\r
9504   DWORD bufsiz = MSG_SIZ;\r
9505 \r
9506   if (!GetComputerName(buf, &bufsiz)) {\r
9507     /*DisplayError("Error getting host name", GetLastError());*/\r
9508     strcpy(buf, "Unknown");\r
9509   }\r
9510   return buf;\r
9511 }\r
9512 \r
9513 \r
9514 int\r
9515 ClockTimerRunning()\r
9516 {\r
9517   return clockTimerEvent != 0;\r
9518 }\r
9519 \r
9520 int\r
9521 StopClockTimer()\r
9522 {\r
9523   if (clockTimerEvent == 0) return FALSE;\r
9524   KillTimer(hwndMain, clockTimerEvent);\r
9525   clockTimerEvent = 0;\r
9526   return TRUE;\r
9527 }\r
9528 \r
9529 void\r
9530 StartClockTimer(long millisec)\r
9531 {\r
9532   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9533                              (UINT) millisec, NULL);\r
9534 }\r
9535 \r
9536 void\r
9537 DisplayWhiteClock(long timeRemaining, int highlight)\r
9538 {\r
9539   HDC hdc;\r
9540   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9541 \r
9542   if(appData.noGUI) return;\r
9543   hdc = GetDC(hwndMain);\r
9544   if (!IsIconic(hwndMain)) {\r
9545     DisplayAClock(hdc, timeRemaining, highlight, \r
9546                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9547   }\r
9548   if (highlight && iconCurrent == iconBlack) {\r
9549     iconCurrent = iconWhite;\r
9550     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9551     if (IsIconic(hwndMain)) {\r
9552       DrawIcon(hdc, 2, 2, iconCurrent);\r
9553     }\r
9554   }\r
9555   (void) ReleaseDC(hwndMain, hdc);\r
9556   if (hwndConsole)\r
9557     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9558 }\r
9559 \r
9560 void\r
9561 DisplayBlackClock(long timeRemaining, int highlight)\r
9562 {\r
9563   HDC hdc;\r
9564   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9565 \r
9566   if(appData.noGUI) return;\r
9567   hdc = GetDC(hwndMain);\r
9568   if (!IsIconic(hwndMain)) {\r
9569     DisplayAClock(hdc, timeRemaining, highlight, \r
9570                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9571   }\r
9572   if (highlight && iconCurrent == iconWhite) {\r
9573     iconCurrent = iconBlack;\r
9574     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9575     if (IsIconic(hwndMain)) {\r
9576       DrawIcon(hdc, 2, 2, iconCurrent);\r
9577     }\r
9578   }\r
9579   (void) ReleaseDC(hwndMain, hdc);\r
9580   if (hwndConsole)\r
9581     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9582 }\r
9583 \r
9584 \r
9585 int\r
9586 LoadGameTimerRunning()\r
9587 {\r
9588   return loadGameTimerEvent != 0;\r
9589 }\r
9590 \r
9591 int\r
9592 StopLoadGameTimer()\r
9593 {\r
9594   if (loadGameTimerEvent == 0) return FALSE;\r
9595   KillTimer(hwndMain, loadGameTimerEvent);\r
9596   loadGameTimerEvent = 0;\r
9597   return TRUE;\r
9598 }\r
9599 \r
9600 void\r
9601 StartLoadGameTimer(long millisec)\r
9602 {\r
9603   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9604                                 (UINT) millisec, NULL);\r
9605 }\r
9606 \r
9607 void\r
9608 AutoSaveGame()\r
9609 {\r
9610   char *defName;\r
9611   FILE *f;\r
9612   char fileTitle[MSG_SIZ];\r
9613 \r
9614   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9615   f = OpenFileDialog(hwndMain, "a", defName,\r
9616                      appData.oldSaveStyle ? "gam" : "pgn",\r
9617                      GAME_FILT, \r
9618                      "Save Game to File", NULL, fileTitle, NULL);\r
9619   if (f != NULL) {\r
9620     SaveGame(f, 0, "");\r
9621     fclose(f);\r
9622   }\r
9623 }\r
9624 \r
9625 \r
9626 void\r
9627 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9628 {\r
9629   if (delayedTimerEvent != 0) {\r
9630     if (appData.debugMode) {\r
9631       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9632     }\r
9633     KillTimer(hwndMain, delayedTimerEvent);\r
9634     delayedTimerEvent = 0;\r
9635     delayedTimerCallback();\r
9636   }\r
9637   delayedTimerCallback = cb;\r
9638   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9639                                 (UINT) millisec, NULL);\r
9640 }\r
9641 \r
9642 DelayedEventCallback\r
9643 GetDelayedEvent()\r
9644 {\r
9645   if (delayedTimerEvent) {\r
9646     return delayedTimerCallback;\r
9647   } else {\r
9648     return NULL;\r
9649   }\r
9650 }\r
9651 \r
9652 void\r
9653 CancelDelayedEvent()\r
9654 {\r
9655   if (delayedTimerEvent) {\r
9656     KillTimer(hwndMain, delayedTimerEvent);\r
9657     delayedTimerEvent = 0;\r
9658   }\r
9659 }\r
9660 \r
9661 DWORD GetWin32Priority(int nice)\r
9662 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9663 /*\r
9664 REALTIME_PRIORITY_CLASS     0x00000100\r
9665 HIGH_PRIORITY_CLASS         0x00000080\r
9666 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9667 NORMAL_PRIORITY_CLASS       0x00000020\r
9668 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9669 IDLE_PRIORITY_CLASS         0x00000040\r
9670 */\r
9671         if (nice < -15) return 0x00000080;\r
9672         if (nice < 0)   return 0x00008000;\r
9673         if (nice == 0)  return 0x00000020;\r
9674         if (nice < 15)  return 0x00004000;\r
9675         return 0x00000040;\r
9676 }\r
9677 \r
9678 /* Start a child process running the given program.\r
9679    The process's standard output can be read from "from", and its\r
9680    standard input can be written to "to".\r
9681    Exit with fatal error if anything goes wrong.\r
9682    Returns an opaque pointer that can be used to destroy the process\r
9683    later.\r
9684 */\r
9685 int\r
9686 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9687 {\r
9688 #define BUFSIZE 4096\r
9689 \r
9690   HANDLE hChildStdinRd, hChildStdinWr,\r
9691     hChildStdoutRd, hChildStdoutWr;\r
9692   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9693   SECURITY_ATTRIBUTES saAttr;\r
9694   BOOL fSuccess;\r
9695   PROCESS_INFORMATION piProcInfo;\r
9696   STARTUPINFO siStartInfo;\r
9697   ChildProc *cp;\r
9698   char buf[MSG_SIZ];\r
9699   DWORD err;\r
9700 \r
9701   if (appData.debugMode) {\r
9702     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9703   }\r
9704 \r
9705   *pr = NoProc;\r
9706 \r
9707   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9708   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9709   saAttr.bInheritHandle = TRUE;\r
9710   saAttr.lpSecurityDescriptor = NULL;\r
9711 \r
9712   /*\r
9713    * The steps for redirecting child's STDOUT:\r
9714    *     1. Create anonymous pipe to be STDOUT for child.\r
9715    *     2. Create a noninheritable duplicate of read handle,\r
9716    *         and close the inheritable read handle.\r
9717    */\r
9718 \r
9719   /* Create a pipe for the child's STDOUT. */\r
9720   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9721     return GetLastError();\r
9722   }\r
9723 \r
9724   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9725   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9726                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9727                              FALSE,     /* not inherited */\r
9728                              DUPLICATE_SAME_ACCESS);\r
9729   if (! fSuccess) {\r
9730     return GetLastError();\r
9731   }\r
9732   CloseHandle(hChildStdoutRd);\r
9733 \r
9734   /*\r
9735    * The steps for redirecting child's STDIN:\r
9736    *     1. Create anonymous pipe to be STDIN for child.\r
9737    *     2. Create a noninheritable duplicate of write handle,\r
9738    *         and close the inheritable write handle.\r
9739    */\r
9740 \r
9741   /* Create a pipe for the child's STDIN. */\r
9742   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9743     return GetLastError();\r
9744   }\r
9745 \r
9746   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9747   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9748                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9749                              FALSE,     /* not inherited */\r
9750                              DUPLICATE_SAME_ACCESS);\r
9751   if (! fSuccess) {\r
9752     return GetLastError();\r
9753   }\r
9754   CloseHandle(hChildStdinWr);\r
9755 \r
9756   /* Arrange to (1) look in dir for the child .exe file, and\r
9757    * (2) have dir be the child's working directory.  Interpret\r
9758    * dir relative to the directory WinBoard loaded from. */\r
9759   GetCurrentDirectory(MSG_SIZ, buf);\r
9760   SetCurrentDirectory(installDir);\r
9761   SetCurrentDirectory(dir);\r
9762 \r
9763   /* Now create the child process. */\r
9764 \r
9765   siStartInfo.cb = sizeof(STARTUPINFO);\r
9766   siStartInfo.lpReserved = NULL;\r
9767   siStartInfo.lpDesktop = NULL;\r
9768   siStartInfo.lpTitle = NULL;\r
9769   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9770   siStartInfo.cbReserved2 = 0;\r
9771   siStartInfo.lpReserved2 = NULL;\r
9772   siStartInfo.hStdInput = hChildStdinRd;\r
9773   siStartInfo.hStdOutput = hChildStdoutWr;\r
9774   siStartInfo.hStdError = hChildStdoutWr;\r
9775 \r
9776   fSuccess = CreateProcess(NULL,\r
9777                            cmdLine,        /* command line */\r
9778                            NULL,           /* process security attributes */\r
9779                            NULL,           /* primary thread security attrs */\r
9780                            TRUE,           /* handles are inherited */\r
9781                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9782                            NULL,           /* use parent's environment */\r
9783                            NULL,\r
9784                            &siStartInfo, /* STARTUPINFO pointer */\r
9785                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9786 \r
9787   err = GetLastError();\r
9788   SetCurrentDirectory(buf); /* return to prev directory */\r
9789   if (! fSuccess) {\r
9790     return err;\r
9791   }\r
9792 \r
9793   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9794     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9795     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9796   }\r
9797 \r
9798   /* Close the handles we don't need in the parent */\r
9799   CloseHandle(piProcInfo.hThread);\r
9800   CloseHandle(hChildStdinRd);\r
9801   CloseHandle(hChildStdoutWr);\r
9802 \r
9803   /* Prepare return value */\r
9804   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9805   cp->kind = CPReal;\r
9806   cp->hProcess = piProcInfo.hProcess;\r
9807   cp->pid = piProcInfo.dwProcessId;\r
9808   cp->hFrom = hChildStdoutRdDup;\r
9809   cp->hTo = hChildStdinWrDup;\r
9810 \r
9811   *pr = (void *) cp;\r
9812 \r
9813   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9814      2000 where engines sometimes don't see the initial command(s)\r
9815      from WinBoard and hang.  I don't understand how that can happen,\r
9816      but the Sleep is harmless, so I've put it in.  Others have also\r
9817      reported what may be the same problem, so hopefully this will fix\r
9818      it for them too.  */\r
9819   Sleep(500);\r
9820 \r
9821   return NO_ERROR;\r
9822 }\r
9823 \r
9824 \r
9825 void\r
9826 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9827 {\r
9828   ChildProc *cp; int result;\r
9829 \r
9830   cp = (ChildProc *) pr;\r
9831   if (cp == NULL) return;\r
9832 \r
9833   switch (cp->kind) {\r
9834   case CPReal:\r
9835     /* TerminateProcess is considered harmful, so... */\r
9836     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9837     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9838     /* The following doesn't work because the chess program\r
9839        doesn't "have the same console" as WinBoard.  Maybe\r
9840        we could arrange for this even though neither WinBoard\r
9841        nor the chess program uses a console for stdio? */\r
9842     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9843 \r
9844     /* [AS] Special termination modes for misbehaving programs... */\r
9845     if( signal == 9 ) { \r
9846         result = TerminateProcess( cp->hProcess, 0 );\r
9847 \r
9848         if ( appData.debugMode) {\r
9849             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9850         }\r
9851     }\r
9852     else if( signal == 10 ) {\r
9853         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9854 \r
9855         if( dw != WAIT_OBJECT_0 ) {\r
9856             result = TerminateProcess( cp->hProcess, 0 );\r
9857 \r
9858             if ( appData.debugMode) {\r
9859                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9860             }\r
9861 \r
9862         }\r
9863     }\r
9864 \r
9865     CloseHandle(cp->hProcess);\r
9866     break;\r
9867 \r
9868   case CPComm:\r
9869     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9870     break;\r
9871 \r
9872   case CPSock:\r
9873     closesocket(cp->sock);\r
9874     WSACleanup();\r
9875     break;\r
9876 \r
9877   case CPRcmd:\r
9878     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9879     closesocket(cp->sock);\r
9880     closesocket(cp->sock2);\r
9881     WSACleanup();\r
9882     break;\r
9883   }\r
9884   free(cp);\r
9885 }\r
9886 \r
9887 void\r
9888 InterruptChildProcess(ProcRef pr)\r
9889 {\r
9890   ChildProc *cp;\r
9891 \r
9892   cp = (ChildProc *) pr;\r
9893   if (cp == NULL) return;\r
9894   switch (cp->kind) {\r
9895   case CPReal:\r
9896     /* The following doesn't work because the chess program\r
9897        doesn't "have the same console" as WinBoard.  Maybe\r
9898        we could arrange for this even though neither WinBoard\r
9899        nor the chess program uses a console for stdio */\r
9900     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9901     break;\r
9902 \r
9903   case CPComm:\r
9904   case CPSock:\r
9905     /* Can't interrupt */\r
9906     break;\r
9907 \r
9908   case CPRcmd:\r
9909     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9910     break;\r
9911   }\r
9912 }\r
9913 \r
9914 \r
9915 int\r
9916 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9917 {\r
9918   char cmdLine[MSG_SIZ];\r
9919 \r
9920   if (port[0] == NULLCHAR) {\r
9921     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9922   } else {\r
9923     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9924   }\r
9925   return StartChildProcess(cmdLine, "", pr);\r
9926 }\r
9927 \r
9928 \r
9929 /* Code to open TCP sockets */\r
9930 \r
9931 int\r
9932 OpenTCP(char *host, char *port, ProcRef *pr)\r
9933 {\r
9934   ChildProc *cp;\r
9935   int err;\r
9936   SOCKET s;\r
9937   struct sockaddr_in sa, mysa;\r
9938   struct hostent FAR *hp;\r
9939   unsigned short uport;\r
9940   WORD wVersionRequested;\r
9941   WSADATA wsaData;\r
9942 \r
9943   /* Initialize socket DLL */\r
9944   wVersionRequested = MAKEWORD(1, 1);\r
9945   err = WSAStartup(wVersionRequested, &wsaData);\r
9946   if (err != 0) return err;\r
9947 \r
9948   /* Make socket */\r
9949   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9950     err = WSAGetLastError();\r
9951     WSACleanup();\r
9952     return err;\r
9953   }\r
9954 \r
9955   /* Bind local address using (mostly) don't-care values.\r
9956    */\r
9957   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9958   mysa.sin_family = AF_INET;\r
9959   mysa.sin_addr.s_addr = INADDR_ANY;\r
9960   uport = (unsigned short) 0;\r
9961   mysa.sin_port = htons(uport);\r
9962   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9963       == SOCKET_ERROR) {\r
9964     err = WSAGetLastError();\r
9965     WSACleanup();\r
9966     return err;\r
9967   }\r
9968 \r
9969   /* Resolve remote host name */\r
9970   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9971   if (!(hp = gethostbyname(host))) {\r
9972     unsigned int b0, b1, b2, b3;\r
9973 \r
9974     err = WSAGetLastError();\r
9975 \r
9976     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9977       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9978       hp->h_addrtype = AF_INET;\r
9979       hp->h_length = 4;\r
9980       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9981       hp->h_addr_list[0] = (char *) malloc(4);\r
9982       hp->h_addr_list[0][0] = (char) b0;\r
9983       hp->h_addr_list[0][1] = (char) b1;\r
9984       hp->h_addr_list[0][2] = (char) b2;\r
9985       hp->h_addr_list[0][3] = (char) b3;\r
9986     } else {\r
9987       WSACleanup();\r
9988       return err;\r
9989     }\r
9990   }\r
9991   sa.sin_family = hp->h_addrtype;\r
9992   uport = (unsigned short) atoi(port);\r
9993   sa.sin_port = htons(uport);\r
9994   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9995 \r
9996   /* Make connection */\r
9997   if (connect(s, (struct sockaddr *) &sa,\r
9998               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9999     err = WSAGetLastError();\r
10000     WSACleanup();\r
10001     return err;\r
10002   }\r
10003 \r
10004   /* Prepare return value */\r
10005   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10006   cp->kind = CPSock;\r
10007   cp->sock = s;\r
10008   *pr = (ProcRef *) cp;\r
10009 \r
10010   return NO_ERROR;\r
10011 }\r
10012 \r
10013 int\r
10014 OpenCommPort(char *name, ProcRef *pr)\r
10015 {\r
10016   HANDLE h;\r
10017   COMMTIMEOUTS ct;\r
10018   ChildProc *cp;\r
10019   char fullname[MSG_SIZ];\r
10020 \r
10021   if (*name != '\\')\r
10022     sprintf(fullname, "\\\\.\\%s", name);\r
10023   else\r
10024     strcpy(fullname, name);\r
10025 \r
10026   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10027                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10028   if (h == (HANDLE) -1) {\r
10029     return GetLastError();\r
10030   }\r
10031   hCommPort = h;\r
10032 \r
10033   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10034 \r
10035   /* Accumulate characters until a 100ms pause, then parse */\r
10036   ct.ReadIntervalTimeout = 100;\r
10037   ct.ReadTotalTimeoutMultiplier = 0;\r
10038   ct.ReadTotalTimeoutConstant = 0;\r
10039   ct.WriteTotalTimeoutMultiplier = 0;\r
10040   ct.WriteTotalTimeoutConstant = 0;\r
10041   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10042 \r
10043   /* Prepare return value */\r
10044   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10045   cp->kind = CPComm;\r
10046   cp->hFrom = h;\r
10047   cp->hTo = h;\r
10048   *pr = (ProcRef *) cp;\r
10049 \r
10050   return NO_ERROR;\r
10051 }\r
10052 \r
10053 int\r
10054 OpenLoopback(ProcRef *pr)\r
10055 {\r
10056   DisplayFatalError("Not implemented", 0, 1);\r
10057   return NO_ERROR;\r
10058 }\r
10059 \r
10060 \r
10061 int\r
10062 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10063 {\r
10064   ChildProc *cp;\r
10065   int err;\r
10066   SOCKET s, s2, s3;\r
10067   struct sockaddr_in sa, mysa;\r
10068   struct hostent FAR *hp;\r
10069   unsigned short uport;\r
10070   WORD wVersionRequested;\r
10071   WSADATA wsaData;\r
10072   int fromPort;\r
10073   char stderrPortStr[MSG_SIZ];\r
10074 \r
10075   /* Initialize socket DLL */\r
10076   wVersionRequested = MAKEWORD(1, 1);\r
10077   err = WSAStartup(wVersionRequested, &wsaData);\r
10078   if (err != 0) return err;\r
10079 \r
10080   /* Resolve remote host name */\r
10081   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10082   if (!(hp = gethostbyname(host))) {\r
10083     unsigned int b0, b1, b2, b3;\r
10084 \r
10085     err = WSAGetLastError();\r
10086 \r
10087     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10088       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10089       hp->h_addrtype = AF_INET;\r
10090       hp->h_length = 4;\r
10091       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10092       hp->h_addr_list[0] = (char *) malloc(4);\r
10093       hp->h_addr_list[0][0] = (char) b0;\r
10094       hp->h_addr_list[0][1] = (char) b1;\r
10095       hp->h_addr_list[0][2] = (char) b2;\r
10096       hp->h_addr_list[0][3] = (char) b3;\r
10097     } else {\r
10098       WSACleanup();\r
10099       return err;\r
10100     }\r
10101   }\r
10102   sa.sin_family = hp->h_addrtype;\r
10103   uport = (unsigned short) 514;\r
10104   sa.sin_port = htons(uport);\r
10105   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10106 \r
10107   /* Bind local socket to unused "privileged" port address\r
10108    */\r
10109   s = INVALID_SOCKET;\r
10110   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10111   mysa.sin_family = AF_INET;\r
10112   mysa.sin_addr.s_addr = INADDR_ANY;\r
10113   for (fromPort = 1023;; fromPort--) {\r
10114     if (fromPort < 0) {\r
10115       WSACleanup();\r
10116       return WSAEADDRINUSE;\r
10117     }\r
10118     if (s == INVALID_SOCKET) {\r
10119       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10120         err = WSAGetLastError();\r
10121         WSACleanup();\r
10122         return err;\r
10123       }\r
10124     }\r
10125     uport = (unsigned short) fromPort;\r
10126     mysa.sin_port = htons(uport);\r
10127     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10128         == SOCKET_ERROR) {\r
10129       err = WSAGetLastError();\r
10130       if (err == WSAEADDRINUSE) continue;\r
10131       WSACleanup();\r
10132       return err;\r
10133     }\r
10134     if (connect(s, (struct sockaddr *) &sa,\r
10135       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10136       err = WSAGetLastError();\r
10137       if (err == WSAEADDRINUSE) {\r
10138         closesocket(s);\r
10139         s = -1;\r
10140         continue;\r
10141       }\r
10142       WSACleanup();\r
10143       return err;\r
10144     }\r
10145     break;\r
10146   }\r
10147 \r
10148   /* Bind stderr local socket to unused "privileged" port address\r
10149    */\r
10150   s2 = INVALID_SOCKET;\r
10151   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10152   mysa.sin_family = AF_INET;\r
10153   mysa.sin_addr.s_addr = INADDR_ANY;\r
10154   for (fromPort = 1023;; fromPort--) {\r
10155     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10156     if (fromPort < 0) {\r
10157       (void) closesocket(s);\r
10158       WSACleanup();\r
10159       return WSAEADDRINUSE;\r
10160     }\r
10161     if (s2 == INVALID_SOCKET) {\r
10162       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10163         err = WSAGetLastError();\r
10164         closesocket(s);\r
10165         WSACleanup();\r
10166         return err;\r
10167       }\r
10168     }\r
10169     uport = (unsigned short) fromPort;\r
10170     mysa.sin_port = htons(uport);\r
10171     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10172         == SOCKET_ERROR) {\r
10173       err = WSAGetLastError();\r
10174       if (err == WSAEADDRINUSE) continue;\r
10175       (void) closesocket(s);\r
10176       WSACleanup();\r
10177       return err;\r
10178     }\r
10179     if (listen(s2, 1) == SOCKET_ERROR) {\r
10180       err = WSAGetLastError();\r
10181       if (err == WSAEADDRINUSE) {\r
10182         closesocket(s2);\r
10183         s2 = INVALID_SOCKET;\r
10184         continue;\r
10185       }\r
10186       (void) closesocket(s);\r
10187       (void) closesocket(s2);\r
10188       WSACleanup();\r
10189       return err;\r
10190     }\r
10191     break;\r
10192   }\r
10193   prevStderrPort = fromPort; // remember port used\r
10194   sprintf(stderrPortStr, "%d", fromPort);\r
10195 \r
10196   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10197     err = WSAGetLastError();\r
10198     (void) closesocket(s);\r
10199     (void) closesocket(s2);\r
10200     WSACleanup();\r
10201     return err;\r
10202   }\r
10203 \r
10204   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10205     err = WSAGetLastError();\r
10206     (void) closesocket(s);\r
10207     (void) closesocket(s2);\r
10208     WSACleanup();\r
10209     return err;\r
10210   }\r
10211   if (*user == NULLCHAR) user = UserName();\r
10212   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10213     err = WSAGetLastError();\r
10214     (void) closesocket(s);\r
10215     (void) closesocket(s2);\r
10216     WSACleanup();\r
10217     return err;\r
10218   }\r
10219   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10220     err = WSAGetLastError();\r
10221     (void) closesocket(s);\r
10222     (void) closesocket(s2);\r
10223     WSACleanup();\r
10224     return err;\r
10225   }\r
10226 \r
10227   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10228     err = WSAGetLastError();\r
10229     (void) closesocket(s);\r
10230     (void) closesocket(s2);\r
10231     WSACleanup();\r
10232     return err;\r
10233   }\r
10234   (void) closesocket(s2);  /* Stop listening */\r
10235 \r
10236   /* Prepare return value */\r
10237   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10238   cp->kind = CPRcmd;\r
10239   cp->sock = s;\r
10240   cp->sock2 = s3;\r
10241   *pr = (ProcRef *) cp;\r
10242 \r
10243   return NO_ERROR;\r
10244 }\r
10245 \r
10246 \r
10247 InputSourceRef\r
10248 AddInputSource(ProcRef pr, int lineByLine,\r
10249                InputCallback func, VOIDSTAR closure)\r
10250 {\r
10251   InputSource *is, *is2 = NULL;\r
10252   ChildProc *cp = (ChildProc *) pr;\r
10253 \r
10254   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10255   is->lineByLine = lineByLine;\r
10256   is->func = func;\r
10257   is->closure = closure;\r
10258   is->second = NULL;\r
10259   is->next = is->buf;\r
10260   if (pr == NoProc) {\r
10261     is->kind = CPReal;\r
10262     consoleInputSource = is;\r
10263   } else {\r
10264     is->kind = cp->kind;\r
10265     /* \r
10266         [AS] Try to avoid a race condition if the thread is given control too early:\r
10267         we create all threads suspended so that the is->hThread variable can be\r
10268         safely assigned, then let the threads start with ResumeThread.\r
10269     */\r
10270     switch (cp->kind) {\r
10271     case CPReal:\r
10272       is->hFile = cp->hFrom;\r
10273       cp->hFrom = NULL; /* now owned by InputThread */\r
10274       is->hThread =\r
10275         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10276                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10277       break;\r
10278 \r
10279     case CPComm:\r
10280       is->hFile = cp->hFrom;\r
10281       cp->hFrom = NULL; /* now owned by InputThread */\r
10282       is->hThread =\r
10283         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10284                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10285       break;\r
10286 \r
10287     case CPSock:\r
10288       is->sock = cp->sock;\r
10289       is->hThread =\r
10290         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10291                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10292       break;\r
10293 \r
10294     case CPRcmd:\r
10295       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10296       *is2 = *is;\r
10297       is->sock = cp->sock;\r
10298       is->second = is2;\r
10299       is2->sock = cp->sock2;\r
10300       is2->second = is2;\r
10301       is->hThread =\r
10302         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10303                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10304       is2->hThread =\r
10305         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10306                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10307       break;\r
10308     }\r
10309 \r
10310     if( is->hThread != NULL ) {\r
10311         ResumeThread( is->hThread );\r
10312     }\r
10313 \r
10314     if( is2 != NULL && is2->hThread != NULL ) {\r
10315         ResumeThread( is2->hThread );\r
10316     }\r
10317   }\r
10318 \r
10319   return (InputSourceRef) is;\r
10320 }\r
10321 \r
10322 void\r
10323 RemoveInputSource(InputSourceRef isr)\r
10324 {\r
10325   InputSource *is;\r
10326 \r
10327   is = (InputSource *) isr;\r
10328   is->hThread = NULL;  /* tell thread to stop */\r
10329   CloseHandle(is->hThread);\r
10330   if (is->second != NULL) {\r
10331     is->second->hThread = NULL;\r
10332     CloseHandle(is->second->hThread);\r
10333   }\r
10334 }\r
10335 \r
10336 \r
10337 int\r
10338 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10339 {\r
10340   DWORD dOutCount;\r
10341   int outCount = SOCKET_ERROR;\r
10342   ChildProc *cp = (ChildProc *) pr;\r
10343   static OVERLAPPED ovl;\r
10344 \r
10345   if (pr == NoProc) {\r
10346     ConsoleOutput(message, count, FALSE);\r
10347     return count;\r
10348   } \r
10349 \r
10350   if (ovl.hEvent == NULL) {\r
10351     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10352   }\r
10353   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10354 \r
10355   switch (cp->kind) {\r
10356   case CPSock:\r
10357   case CPRcmd:\r
10358     outCount = send(cp->sock, message, count, 0);\r
10359     if (outCount == SOCKET_ERROR) {\r
10360       *outError = WSAGetLastError();\r
10361     } else {\r
10362       *outError = NO_ERROR;\r
10363     }\r
10364     break;\r
10365 \r
10366   case CPReal:\r
10367     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10368                   &dOutCount, NULL)) {\r
10369       *outError = NO_ERROR;\r
10370       outCount = (int) dOutCount;\r
10371     } else {\r
10372       *outError = GetLastError();\r
10373     }\r
10374     break;\r
10375 \r
10376   case CPComm:\r
10377     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10378                             &dOutCount, &ovl);\r
10379     if (*outError == NO_ERROR) {\r
10380       outCount = (int) dOutCount;\r
10381     }\r
10382     break;\r
10383   }\r
10384   return outCount;\r
10385 }\r
10386 \r
10387 int\r
10388 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10389                        long msdelay)\r
10390 {\r
10391   /* Ignore delay, not implemented for WinBoard */\r
10392   return OutputToProcess(pr, message, count, outError);\r
10393 }\r
10394 \r
10395 \r
10396 void\r
10397 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10398                         char *buf, int count, int error)\r
10399 {\r
10400   DisplayFatalError("Not implemented", 0, 1);\r
10401 }\r
10402 \r
10403 /* see wgamelist.c for Game List functions */\r
10404 /* see wedittags.c for Edit Tags functions */\r
10405 \r
10406 \r
10407 VOID\r
10408 ICSInitScript()\r
10409 {\r
10410   FILE *f;\r
10411   char buf[MSG_SIZ];\r
10412   char *dummy;\r
10413 \r
10414   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10415     f = fopen(buf, "r");\r
10416     if (f != NULL) {\r
10417       ProcessICSInitScript(f);\r
10418       fclose(f);\r
10419     }\r
10420   }\r
10421 }\r
10422 \r
10423 \r
10424 VOID\r
10425 StartAnalysisClock()\r
10426 {\r
10427   if (analysisTimerEvent) return;\r
10428   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10429                                         (UINT) 2000, NULL);\r
10430 }\r
10431 \r
10432 LRESULT CALLBACK\r
10433 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10434 {\r
10435   static HANDLE hwndText;\r
10436   RECT rect;\r
10437   static int sizeX, sizeY;\r
10438   int newSizeX, newSizeY, flags;\r
10439   MINMAXINFO *mmi;\r
10440 \r
10441   switch (message) {\r
10442   case WM_INITDIALOG: /* message: initialize dialog box */\r
10443     /* Initialize the dialog items */\r
10444     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10445     SetWindowText(hDlg, analysisTitle);\r
10446     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10447     /* Size and position the dialog */\r
10448     if (!analysisDialog) {\r
10449       analysisDialog = hDlg;\r
10450       flags = SWP_NOZORDER;\r
10451       GetClientRect(hDlg, &rect);\r
10452       sizeX = rect.right;\r
10453       sizeY = rect.bottom;\r
10454       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10455           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10456         WINDOWPLACEMENT wp;\r
10457         EnsureOnScreen(&analysisX, &analysisY);\r
10458         wp.length = sizeof(WINDOWPLACEMENT);\r
10459         wp.flags = 0;\r
10460         wp.showCmd = SW_SHOW;\r
10461         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10462         wp.rcNormalPosition.left = analysisX;\r
10463         wp.rcNormalPosition.right = analysisX + analysisW;\r
10464         wp.rcNormalPosition.top = analysisY;\r
10465         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10466         SetWindowPlacement(hDlg, &wp);\r
10467 \r
10468         GetClientRect(hDlg, &rect);\r
10469         newSizeX = rect.right;\r
10470         newSizeY = rect.bottom;\r
10471         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10472                               newSizeX, newSizeY);\r
10473         sizeX = newSizeX;\r
10474         sizeY = newSizeY;\r
10475       }\r
10476     }\r
10477     return FALSE;\r
10478 \r
10479   case WM_COMMAND: /* message: received a command */\r
10480     switch (LOWORD(wParam)) {\r
10481     case IDCANCEL:\r
10482       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10483           ExitAnalyzeMode();\r
10484           ModeHighlight();\r
10485           return TRUE;\r
10486       }\r
10487       EditGameEvent();\r
10488       return TRUE;\r
10489     default:\r
10490       break;\r
10491     }\r
10492     break;\r
10493 \r
10494   case WM_SIZE:\r
10495     newSizeX = LOWORD(lParam);\r
10496     newSizeY = HIWORD(lParam);\r
10497     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10498     sizeX = newSizeX;\r
10499     sizeY = newSizeY;\r
10500     break;\r
10501 \r
10502   case WM_GETMINMAXINFO:\r
10503     /* Prevent resizing window too small */\r
10504     mmi = (MINMAXINFO *) lParam;\r
10505     mmi->ptMinTrackSize.x = 100;\r
10506     mmi->ptMinTrackSize.y = 100;\r
10507     break;\r
10508   }\r
10509   return FALSE;\r
10510 }\r
10511 \r
10512 VOID\r
10513 AnalysisPopUp(char* title, char* str)\r
10514 {\r
10515   FARPROC lpProc;\r
10516   char *p, *q;\r
10517 \r
10518   /* [AS] */\r
10519   EngineOutputPopUp();\r
10520   return;\r
10521 \r
10522   if (str == NULL) str = "";\r
10523   p = (char *) malloc(2 * strlen(str) + 2);\r
10524   q = p;\r
10525   while (*str) {\r
10526     if (*str == '\n') *q++ = '\r';\r
10527     *q++ = *str++;\r
10528   }\r
10529   *q = NULLCHAR;\r
10530   if (analysisText != NULL) free(analysisText);\r
10531   analysisText = p;\r
10532 \r
10533   if (analysisDialog) {\r
10534     SetWindowText(analysisDialog, title);\r
10535     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10536     ShowWindow(analysisDialog, SW_SHOW);\r
10537   } else {\r
10538     analysisTitle = title;\r
10539     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10540     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10541                  hwndMain, (DLGPROC)lpProc);\r
10542     FreeProcInstance(lpProc);\r
10543   }\r
10544   analysisDialogUp = TRUE;  \r
10545 }\r
10546 \r
10547 VOID\r
10548 AnalysisPopDown()\r
10549 {\r
10550   if (analysisDialog) {\r
10551     ShowWindow(analysisDialog, SW_HIDE);\r
10552   }\r
10553   analysisDialogUp = FALSE;  \r
10554 }\r
10555 \r
10556 \r
10557 VOID\r
10558 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10559 {\r
10560   highlightInfo.sq[0].x = fromX;\r
10561   highlightInfo.sq[0].y = fromY;\r
10562   highlightInfo.sq[1].x = toX;\r
10563   highlightInfo.sq[1].y = toY;\r
10564 }\r
10565 \r
10566 VOID\r
10567 ClearHighlights()\r
10568 {\r
10569   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10570     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10571 }\r
10572 \r
10573 VOID\r
10574 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10575 {\r
10576   premoveHighlightInfo.sq[0].x = fromX;\r
10577   premoveHighlightInfo.sq[0].y = fromY;\r
10578   premoveHighlightInfo.sq[1].x = toX;\r
10579   premoveHighlightInfo.sq[1].y = toY;\r
10580 }\r
10581 \r
10582 VOID\r
10583 ClearPremoveHighlights()\r
10584 {\r
10585   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10586     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10587 }\r
10588 \r
10589 VOID\r
10590 ShutDownFrontEnd()\r
10591 {\r
10592   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10593   DeleteClipboardTempFiles();\r
10594 }\r
10595 \r
10596 void\r
10597 BoardToTop()\r
10598 {\r
10599     if (IsIconic(hwndMain))\r
10600       ShowWindow(hwndMain, SW_RESTORE);\r
10601 \r
10602     SetActiveWindow(hwndMain);\r
10603 }\r
10604 \r
10605 /*\r
10606  * Prototypes for animation support routines\r
10607  */\r
10608 static void ScreenSquare(int column, int row, POINT * pt);\r
10609 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10610      POINT frames[], int * nFrames);\r
10611 \r
10612 \r
10613 void\r
10614 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10615 {       // [HGM] atomic: animate blast wave\r
10616         int i;\r
10617 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10618         explodeInfo.fromX = fromX;\r
10619         explodeInfo.fromY = fromY;\r
10620         explodeInfo.toX = toX;\r
10621         explodeInfo.toY = toY;\r
10622         for(i=1; i<nFrames; i++) {\r
10623             explodeInfo.radius = (i*180)/(nFrames-1);\r
10624             DrawPosition(FALSE, NULL);\r
10625             Sleep(appData.animSpeed);\r
10626         }\r
10627         explodeInfo.radius = 0;\r
10628         DrawPosition(TRUE, NULL);\r
10629 }\r
10630 \r
10631 #define kFactor 4\r
10632 \r
10633 void\r
10634 AnimateMove(board, fromX, fromY, toX, toY)\r
10635      Board board;\r
10636      int fromX;\r
10637      int fromY;\r
10638      int toX;\r
10639      int toY;\r
10640 {\r
10641   ChessSquare piece;\r
10642   POINT start, finish, mid;\r
10643   POINT frames[kFactor * 2 + 1];\r
10644   int nFrames, n;\r
10645 \r
10646   if (!appData.animate) return;\r
10647   if (doingSizing) return;\r
10648   if (fromY < 0 || fromX < 0) return;\r
10649   piece = board[fromY][fromX];\r
10650   if (piece >= EmptySquare) return;\r
10651 \r
10652   ScreenSquare(fromX, fromY, &start);\r
10653   ScreenSquare(toX, toY, &finish);\r
10654 \r
10655   /* All pieces except knights move in straight line */\r
10656   if (piece != WhiteKnight && piece != BlackKnight) {\r
10657     mid.x = start.x + (finish.x - start.x) / 2;\r
10658     mid.y = start.y + (finish.y - start.y) / 2;\r
10659   } else {\r
10660     /* Knight: make diagonal movement then straight */\r
10661     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10662        mid.x = start.x + (finish.x - start.x) / 2;\r
10663        mid.y = finish.y;\r
10664      } else {\r
10665        mid.x = finish.x;\r
10666        mid.y = start.y + (finish.y - start.y) / 2;\r
10667      }\r
10668   }\r
10669   \r
10670   /* Don't use as many frames for very short moves */\r
10671   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10672     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10673   else\r
10674     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10675 \r
10676   animInfo.from.x = fromX;\r
10677   animInfo.from.y = fromY;\r
10678   animInfo.to.x = toX;\r
10679   animInfo.to.y = toY;\r
10680   animInfo.lastpos = start;\r
10681   animInfo.piece = piece;\r
10682   for (n = 0; n < nFrames; n++) {\r
10683     animInfo.pos = frames[n];\r
10684     DrawPosition(FALSE, NULL);\r
10685     animInfo.lastpos = animInfo.pos;\r
10686     Sleep(appData.animSpeed);\r
10687   }\r
10688   animInfo.pos = finish;\r
10689   DrawPosition(FALSE, NULL);\r
10690   animInfo.piece = EmptySquare;\r
10691   if(gameInfo.variant == VariantAtomic && \r
10692      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10693         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10694 }\r
10695 \r
10696 /*      Convert board position to corner of screen rect and color       */\r
10697 \r
10698 static void\r
10699 ScreenSquare(column, row, pt)\r
10700      int column; int row; POINT * pt;\r
10701 {\r
10702   if (flipView) {\r
10703     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10704     pt->y = lineGap + row * (squareSize + lineGap);\r
10705   } else {\r
10706     pt->x = lineGap + column * (squareSize + lineGap);\r
10707     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10708   }\r
10709 }\r
10710 \r
10711 /*      Generate a series of frame coords from start->mid->finish.\r
10712         The movement rate doubles until the half way point is\r
10713         reached, then halves back down to the final destination,\r
10714         which gives a nice slow in/out effect. The algorithmn\r
10715         may seem to generate too many intermediates for short\r
10716         moves, but remember that the purpose is to attract the\r
10717         viewers attention to the piece about to be moved and\r
10718         then to where it ends up. Too few frames would be less\r
10719         noticeable.                                             */\r
10720 \r
10721 static void\r
10722 Tween(start, mid, finish, factor, frames, nFrames)\r
10723      POINT * start; POINT * mid;\r
10724      POINT * finish; int factor;\r
10725      POINT frames[]; int * nFrames;\r
10726 {\r
10727   int n, fraction = 1, count = 0;\r
10728 \r
10729   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10730   for (n = 0; n < factor; n++)\r
10731     fraction *= 2;\r
10732   for (n = 0; n < factor; n++) {\r
10733     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10734     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10735     count ++;\r
10736     fraction = fraction / 2;\r
10737   }\r
10738   \r
10739   /* Midpoint */\r
10740   frames[count] = *mid;\r
10741   count ++;\r
10742   \r
10743   /* Slow out, stepping 1/2, then 1/4, ... */\r
10744   fraction = 2;\r
10745   for (n = 0; n < factor; n++) {\r
10746     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10747     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10748     count ++;\r
10749     fraction = fraction * 2;\r
10750   }\r
10751   *nFrames = count;\r
10752 }\r
10753 \r
10754 void\r
10755 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10756 {\r
10757 #if 0\r
10758     char buf[256];\r
10759 \r
10760     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10761         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10762 \r
10763     OutputDebugString( buf );\r
10764 #endif\r
10765 \r
10766     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10767 \r
10768     EvalGraphSet( first, last, current, pvInfoList );\r
10769 }\r
10770 \r
10771 void SetProgramStats( FrontEndProgramStats * stats )\r
10772 {\r
10773 #if 0\r
10774     char buf[1024];\r
10775 \r
10776     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10777         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10778 \r
10779     OutputDebugString( buf );\r
10780 #endif\r
10781 \r
10782     EngineOutputUpdate( stats );\r
10783 }\r