added option for work-around for some FRC engines in regards to FRCFENs
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int x, y, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo = {0, 0, 0};\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 \r
205 /* [AS] Support for background textures */\r
206 #define BACK_TEXTURE_MODE_DISABLED      0\r
207 #define BACK_TEXTURE_MODE_PLAIN         1\r
208 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #define oldDialog (_winmajor < 4)\r
221 #endif\r
222 \r
223 char *defaultTextAttribs[] = \r
224 {\r
225   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
226   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
227   COLOR_NONE\r
228 };\r
229 \r
230 typedef struct {\r
231   char *name;\r
232   int squareSize;\r
233   int lineGap;\r
234   int smallLayout;\r
235   int tinyLayout;\r
236   int cliWidth, cliHeight;\r
237 } SizeInfo;\r
238 \r
239 SizeInfo sizeInfo[] = \r
240 {\r
241   { "tiny",     21, 0, 1, 1, 0, 0 },\r
242   { "teeny",    25, 1, 1, 1, 0, 0 },\r
243   { "dinky",    29, 1, 1, 1, 0, 0 },\r
244   { "petite",   33, 1, 1, 1, 0, 0 },\r
245   { "slim",     37, 2, 1, 0, 0, 0 },\r
246   { "small",    40, 2, 1, 0, 0, 0 },\r
247   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
248   { "middling", 49, 2, 0, 0, 0, 0 },\r
249   { "average",  54, 2, 0, 0, 0, 0 },\r
250   { "moderate", 58, 3, 0, 0, 0, 0 },\r
251   { "medium",   64, 3, 0, 0, 0, 0 },\r
252   { "bulky",    72, 3, 0, 0, 0, 0 },\r
253   { "large",    80, 3, 0, 0, 0, 0 },\r
254   { "big",      87, 3, 0, 0, 0, 0 },\r
255   { "huge",     95, 3, 0, 0, 0, 0 },\r
256   { "giant",    108, 3, 0, 0, 0, 0 },\r
257   { "colossal", 116, 4, 0, 0, 0, 0 },\r
258   { "titanic",  129, 4, 0, 0, 0, 0 },\r
259   { NULL, 0, 0, 0, 0, 0, 0 }\r
260 };\r
261 \r
262 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
263 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
264 {\r
265   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
266   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
267   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
283 };\r
284 \r
285 MyFont *font[NUM_SIZES][NUM_FONTS];\r
286 \r
287 typedef struct {\r
288   char *label;\r
289   int id;\r
290   HWND hwnd;\r
291   WNDPROC wndproc;\r
292 } MyButtonDesc;\r
293 \r
294 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
295 #define N_BUTTONS 5\r
296 \r
297 MyButtonDesc buttonDesc[N_BUTTONS] =\r
298 {\r
299   {"<<", IDM_ToStart, NULL, NULL},\r
300   {"<", IDM_Backward, NULL, NULL},\r
301   {"P", IDM_Pause, NULL, NULL},\r
302   {">", IDM_Forward, NULL, NULL},\r
303   {">>", IDM_ToEnd, NULL, NULL},\r
304 };\r
305 \r
306 int tinyLayout = 0, smallLayout = 0;\r
307 #define MENU_BAR_ITEMS 6\r
308 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
309   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
310   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
311 };\r
312 \r
313 \r
314 MySound sounds[(int)NSoundClasses];\r
315 MyTextAttribs textAttribs[(int)NColorClasses];\r
316 \r
317 MyColorizeAttribs colorizeAttribs[] = {\r
318   { (COLORREF)0, 0, "Shout Text" },\r
319   { (COLORREF)0, 0, "SShout/CShout" },\r
320   { (COLORREF)0, 0, "Channel 1 Text" },\r
321   { (COLORREF)0, 0, "Channel Text" },\r
322   { (COLORREF)0, 0, "Kibitz Text" },\r
323   { (COLORREF)0, 0, "Tell Text" },\r
324   { (COLORREF)0, 0, "Challenge Text" },\r
325   { (COLORREF)0, 0, "Request Text" },\r
326   { (COLORREF)0, 0, "Seek Text" },\r
327   { (COLORREF)0, 0, "Normal Text" },\r
328   { (COLORREF)0, 0, "None" }\r
329 };\r
330 \r
331 \r
332 \r
333 static char *commentTitle;\r
334 static char *commentText;\r
335 static int commentIndex;\r
336 static Boolean editComment = FALSE;\r
337 HWND commentDialog = NULL;\r
338 BOOLEAN commentDialogUp = FALSE;\r
339 static int commentX, commentY, commentH, commentW;\r
340 \r
341 static char *analysisTitle;\r
342 static char *analysisText;\r
343 HWND analysisDialog = NULL;\r
344 BOOLEAN analysisDialogUp = FALSE;\r
345 static int analysisX, analysisY, analysisH, analysisW;\r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 HWND moveHistoryDialog = NULL;\r
416 BOOLEAN moveHistoryDialogUp = FALSE;\r
417 \r
418 WindowPlacement wpMoveHistory;\r
419 \r
420 HWND evalGraphDialog = NULL;\r
421 BOOLEAN evalGraphDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpEvalGraph;\r
424 \r
425 HWND engineOutputDialog = NULL;\r
426 BOOLEAN engineOutputDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEngineOutput;\r
429 \r
430 VOID MoveHistoryPopUp();\r
431 VOID MoveHistoryPopDown();\r
432 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
433 BOOL MoveHistoryIsUp();\r
434 \r
435 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
436 VOID EvalGraphPopUp();\r
437 VOID EvalGraphPopDown();\r
438 BOOL EvalGraphIsUp();\r
439 \r
440 VOID EngineOutputPopUp();\r
441 VOID EngineOutputPopDown();\r
442 BOOL EngineOutputIsUp();\r
443 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
444 \r
445 VOID GothicPopUp(char *title, VariantClass variant);\r
446 /*\r
447  * Setting "frozen" should disable all user input other than deleting\r
448  * the window.  We do this while engines are initializing themselves.\r
449  */\r
450 static int frozen = 0;\r
451 static int oldMenuItemState[MENU_BAR_ITEMS];\r
452 void FreezeUI()\r
453 {\r
454   HMENU hmenu;\r
455   int i;\r
456 \r
457   if (frozen) return;\r
458   frozen = 1;\r
459   hmenu = GetMenu(hwndMain);\r
460   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
461     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
462   }\r
463   DrawMenuBar(hwndMain);\r
464 }\r
465 \r
466 /* Undo a FreezeUI */\r
467 void ThawUI()\r
468 {\r
469   HMENU hmenu;\r
470   int i;\r
471 \r
472   if (!frozen) return;\r
473   frozen = 0;\r
474   hmenu = GetMenu(hwndMain);\r
475   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
476     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
477   }\r
478   DrawMenuBar(hwndMain);\r
479 }\r
480 \r
481 /*---------------------------------------------------------------------------*\\r
482  *\r
483  * WinMain\r
484  *\r
485 \*---------------------------------------------------------------------------*/\r
486 \r
487 int APIENTRY\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
489         LPSTR lpCmdLine, int nCmdShow)\r
490 {\r
491   MSG msg;\r
492   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
493 //  INITCOMMONCONTROLSEX ex;\r
494 \r
495   debugFP = stderr;\r
496 \r
497   LoadLibrary("RICHED32.DLL");\r
498   consoleCF.cbSize = sizeof(CHARFORMAT);\r
499 \r
500   if (!InitApplication(hInstance)) {\r
501     return (FALSE);\r
502   }\r
503   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
504     return (FALSE);\r
505   }\r
506 \r
507 //  InitCommonControlsEx(&ex);\r
508   InitCommonControls();\r
509 \r
510   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
511   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
512   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
513 \r
514   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
515 \r
516   while (GetMessage(&msg, /* message structure */\r
517                     NULL, /* handle of window receiving the message */\r
518                     0,    /* lowest message to examine */\r
519                     0))   /* highest message to examine */\r
520     {\r
521       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
522           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
523           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
524           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
525           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
526           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
527           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
528           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
529           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
531         TranslateMessage(&msg); /* Translates virtual key codes */\r
532         DispatchMessage(&msg);  /* Dispatches message to window */\r
533       }\r
534     }\r
535 \r
536 \r
537   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
538 }\r
539 \r
540 /*---------------------------------------------------------------------------*\\r
541  *\r
542  * Initialization functions\r
543  *\r
544 \*---------------------------------------------------------------------------*/\r
545 \r
546 BOOL\r
547 InitApplication(HINSTANCE hInstance)\r
548 {\r
549   WNDCLASS wc;\r
550 \r
551   /* Fill in window class structure with parameters that describe the */\r
552   /* main window. */\r
553 \r
554   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
555   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
556   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
557   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
558   wc.hInstance     = hInstance;         /* Owner of this class */\r
559   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
560   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
561   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
562   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
563   wc.lpszClassName = szAppName;                 /* Name to register as */\r
564 \r
565   /* Register the window class and return success/failure code. */\r
566   if (!RegisterClass(&wc)) return FALSE;\r
567 \r
568   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
569   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
570   wc.cbClsExtra    = 0;\r
571   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
572   wc.hInstance     = hInstance;\r
573   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
574   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
575   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
576   wc.lpszMenuName  = NULL;\r
577   wc.lpszClassName = szConsoleName;\r
578 \r
579   if (!RegisterClass(&wc)) return FALSE;\r
580   return TRUE;\r
581 }\r
582 \r
583 \r
584 /* Set by InitInstance, used by EnsureOnScreen */\r
585 int screenHeight, screenWidth;\r
586 \r
587 void\r
588 EnsureOnScreen(int *x, int *y)\r
589 {\r
590 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
591   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
592   if (*x > screenWidth - 32) *x = 0;\r
593   if (*y > screenHeight - 32) *y = 0;\r
594   if (*x < 0) *x = 0;\r
595   if (*y < 0) *y = 0;\r
596 //  if (*x < 10) *x = 10;\r
597 //  if (*y < gap) *y = gap;\r
598 }\r
599 \r
600 BOOL\r
601 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
602 {\r
603   HWND hwnd; /* Main window handle. */\r
604   int ibs;\r
605   WINDOWPLACEMENT wp;\r
606   char *filepart;\r
607 \r
608   hInst = hInstance;    /* Store instance handle in our global variable */\r
609 \r
610   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
611     *filepart = NULLCHAR;\r
612   } else {\r
613     GetCurrentDirectory(MSG_SIZ, installDir);\r
614   }\r
615   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
616   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
617   if (appData.debugMode) {\r
618     debugFP = fopen(appData.nameOfDebugFile, "w");\r
619     setbuf(debugFP, NULL);\r
620   }\r
621 \r
622   InitBackEnd1();\r
623 \r
624 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
625 //  InitEngineUCI( installDir, &second );\r
626 \r
627   /* Create a main window for this application instance. */\r
628   hwnd = CreateWindow(szAppName, szTitle,\r
629                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
630                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
631                       NULL, NULL, hInstance, NULL);\r
632   hwndMain = hwnd;\r
633 \r
634   /* If window could not be created, return "failure" */\r
635   if (!hwnd) {\r
636     return (FALSE);\r
637   }\r
638 \r
639   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
640   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
641       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642 \r
643       if (first.programLogo == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
645       }\r
646   } else if(appData.autoLogo) {\r
647       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
648         char buf[MSG_SIZ];\r
649         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
650         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
651       }\r
652   }\r
653 \r
654   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
655       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
656 \r
657       if (second.programLogo == NULL && appData.debugMode) {\r
658           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
659       }\r
660   } else if(appData.autoLogo) {\r
661       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
662         char buf[MSG_SIZ];\r
663         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
664         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
665       }\r
666   }\r
667 \r
668   iconWhite = LoadIcon(hInstance, "icon_white");\r
669   iconBlack = LoadIcon(hInstance, "icon_black");\r
670   iconCurrent = iconWhite;\r
671   InitDrawingColors();\r
672   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
673   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
674   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
675     /* Compute window size for each board size, and use the largest\r
676        size that fits on this screen as the default. */\r
677     InitDrawingSizes((BoardSize)ibs, 0);\r
678     if (boardSize == (BoardSize)-1 &&\r
679         winHeight <= screenHeight\r
680            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
681         && winWidth <= screenWidth) {\r
682       boardSize = (BoardSize)ibs;\r
683     }\r
684   }\r
685 \r
686   InitDrawingSizes(boardSize, 0);\r
687   InitMenuChecks();\r
688   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
689 \r
690   /* [AS] Load textures if specified */\r
691   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
692   \r
693   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
694       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       liteBackTextureMode = appData.liteBackTextureMode;\r
696 \r
697       if (liteBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
699       }\r
700   }\r
701   \r
702   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
703       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
704       darkBackTextureMode = appData.darkBackTextureMode;\r
705 \r
706       if (darkBackTexture == NULL && appData.debugMode) {\r
707           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
708       }\r
709   }\r
710 \r
711   mysrandom( (unsigned) time(NULL) );\r
712 \r
713   /* [AS] Restore layout */\r
714   if( wpMoveHistory.visible ) {\r
715       MoveHistoryPopUp();\r
716   }\r
717 \r
718   if( wpEvalGraph.visible ) {\r
719       EvalGraphPopUp();\r
720   }\r
721 \r
722   if( wpEngineOutput.visible ) {\r
723       EngineOutputPopUp();\r
724   }\r
725 \r
726   InitBackEnd2();\r
727 \r
728   /* Make the window visible; update its client area; and return "success" */\r
729   EnsureOnScreen(&boardX, &boardY);\r
730   wp.length = sizeof(WINDOWPLACEMENT);\r
731   wp.flags = 0;\r
732   wp.showCmd = nCmdShow;\r
733   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
734   wp.rcNormalPosition.left = boardX;\r
735   wp.rcNormalPosition.right = boardX + winWidth;\r
736   wp.rcNormalPosition.top = boardY;\r
737   wp.rcNormalPosition.bottom = boardY + winHeight;\r
738   SetWindowPlacement(hwndMain, &wp);\r
739 \r
740   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
741                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
742 \r
743 #if 0\r
744   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
745   if( gameInfo.variant != VariantFischeRandom ) {\r
746       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
747   }\r
748 #endif\r
749   if (hwndConsole) {\r
750 #if AOT_CONSOLE\r
751     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
752                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
753 #endif\r
754     ShowWindow(hwndConsole, nCmdShow);\r
755   }\r
756   UpdateWindow(hwnd);\r
757 \r
758   return TRUE;\r
759 \r
760 }\r
761 \r
762 \r
763 typedef enum {\r
764   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
765   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
766   ArgSettingsFilename\r
767 } ArgType;\r
768 \r
769 typedef struct {\r
770   char *argName;\r
771   ArgType argType;\r
772   /***\r
773   union {\r
774     String *pString;       // ArgString\r
775     int *pInt;             // ArgInt\r
776     float *pFloat;         // ArgFloat\r
777     Boolean *pBoolean;     // ArgBoolean\r
778     COLORREF *pColor;      // ArgColor\r
779     ColorClass cc;         // ArgAttribs\r
780     String *pFilename;     // ArgFilename\r
781     BoardSize *pBoardSize; // ArgBoardSize\r
782     int whichFont;         // ArgFont\r
783     DCB *pDCB;             // ArgCommSettings\r
784     String *pFilename;     // ArgSettingsFilename\r
785   } argLoc;\r
786   ***/\r
787   LPVOID argLoc;\r
788   BOOL save;\r
789 } ArgDescriptor;\r
790 \r
791 int junk;\r
792 ArgDescriptor argDescriptors[] = {\r
793   /* positional arguments */\r
794   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
795   { "", ArgNone, NULL },\r
796   /* keyword arguments */\r
797   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
798   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
799   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
800   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
801   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
802   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
803   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
804   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
805   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
806   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
807   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
808   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
809   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
810   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
811   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
812   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
813   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
814   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
815     FALSE },\r
816   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
817     FALSE },\r
818   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
819     FALSE },\r
820   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
821   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
822     FALSE },\r
823   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
824   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
825   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
826   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
827   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
828   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
829   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
830   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
831   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
832   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
833   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
834   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
835   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
836   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
837   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
838   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
839   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
840   /*!!bitmapDirectory?*/\r
841   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
842   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
843   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
844   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
845   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
846   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
847   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
848   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
849   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
850   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
851   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
852   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
853   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
854   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
855   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
856   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
857   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
858   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
859   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
860   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
861   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
862   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
863   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
864   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
865   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
866   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
867   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
868   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
869   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
870   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
871   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
872   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
873   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
874   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
875   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
876   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
877   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
878   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
879   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
880   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
881   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
882   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
883   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
884   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
885   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
886   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
887   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
888   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
889   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
890   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
891   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
892   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
893   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
894   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
895   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
896   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
897   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
898   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
899   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
900   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
901   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
902   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
903   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
904   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
905   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
906   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
907   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
908   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
909   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
910   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
911   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
912   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
913   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
914   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
915   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
916   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
917   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
918   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
919   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
920   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
921   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
922   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
923   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
924   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
925   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
926   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
927   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
928   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
929   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
930   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
931   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
932   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
933   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
934   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
935     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
936   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
937   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
938   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
939   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
940   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
941   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
942   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
943   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
944     TRUE }, /* must come after all fonts */\r
945   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
946   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
947     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
948   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
949   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
950   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
951   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
952   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
953   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
954   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
955   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
956   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
957   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
958   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
959   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
960   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
961   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
962   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
963   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
964   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
965   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
966   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
967   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
968   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
969   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
970   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
971   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
972   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
973   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
974   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
975   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
976 #if 0\r
977   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
978   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
979 #endif\r
980   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
981   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
982   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
983   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
984   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
985   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
986   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
987   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
988   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
989   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
990   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
991   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
992   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
993   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
994   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
995   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
996   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
997   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
998   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
999   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1000   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1001   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1002   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1003   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1004   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1005   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1006   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1007   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1008   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1009   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1010   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1011   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1012   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1013   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1014   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1015   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1016   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1017   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1018   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1019   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1020   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1021   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1022   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1023   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1024   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1025   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1026   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1027   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1028   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1029   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1030   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1031   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1032   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1033   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1034   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1035   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1036   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1037   { "highlightLastMove", ArgBoolean,\r
1038     (LPVOID) &appData.highlightLastMove, TRUE },\r
1039   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1040   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1041   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1042   { "highlightDragging", ArgBoolean,\r
1043     (LPVOID) &appData.highlightDragging, TRUE },\r
1044   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1045   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1046   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1047   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1048   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1049   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1050   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1051   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1052   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1053   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1054   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1055   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1056   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1057   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1058   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1059   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1060   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1061   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1062   { "soundShout", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1064   { "soundSShout", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1066   { "soundChannel1", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1068   { "soundChannel", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1070   { "soundKibitz", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1072   { "soundTell", ArgFilename,\r
1073     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1074   { "soundChallenge", ArgFilename,\r
1075     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1076   { "soundRequest", ArgFilename,\r
1077     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1078   { "soundSeek", ArgFilename,\r
1079     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1080   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1081   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1082   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1083   { "soundIcsLoss", ArgFilename, \r
1084     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1085   { "soundIcsDraw", ArgFilename, \r
1086     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1087   { "soundIcsUnfinished", ArgFilename, \r
1088     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1089   { "soundIcsAlarm", ArgFilename, \r
1090     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1091   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1092   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1093   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1094   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1095   { "reuseChessPrograms", ArgBoolean,\r
1096     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1097   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1098   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1099   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1100   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1101   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1102   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1103   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1104   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1105   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1106   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1107   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1108   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1109   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1110   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1111   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1112   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1113   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1114   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1115   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1116   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1117   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1118   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1119   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1120   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1121   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1122   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1123   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1124   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1125   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1126   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1127   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1128   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1129   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1130   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1131   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1132   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1133   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1134     TRUE },\r
1135   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1136     TRUE },\r
1137   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1138   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1139   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1140   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1141   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1142   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1143   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1144   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1145   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1146   /* [AS] New features */\r
1147   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1148   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1149   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1150   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1151   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1152   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1153   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1154   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1155   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1156   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1157   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1158   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1159   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1160   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1161   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1162   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1163   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1164   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1165   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1166   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1167   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1168   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1169   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1170   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1171   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1172   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1173   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1174   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1175   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1176   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1177   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1178   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1179   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1180   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1181   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1182   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1183   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1184   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1185   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1186   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1187   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1188   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1189   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1190   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1191   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1192   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1193   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1194   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1195   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1196   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1197 \r
1198   /* [AS] Layout stuff */\r
1199   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1200   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1201   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1202   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1203   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1204 \r
1205   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1206   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1207   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1208   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1209   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1210 \r
1211   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1212   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1213   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1214   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1215   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1216 \r
1217   /* [HGM] board-size, adjudication and misc. options */\r
1218   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1219   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1220   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1221   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1222   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1223   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1224   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1225   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1226   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1227   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1228   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1229   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1230   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1231   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1232   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1233   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1234   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1235   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1236   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1237   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1238   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1239   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1240   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1241   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1242   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1243   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1244   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1245   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1246   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1247   { "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   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1290   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1291 #endif\r
1292   /* [HGM] options for broadcasting and time odds */\r
1293   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1294   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1295   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1296   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1297   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1298   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1299   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1300   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1301   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1302   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1303   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1304   { NULL, ArgNone, NULL, FALSE }\r
1305 };\r
1306 \r
1307 \r
1308 /* Kludge for indirection files on command line */\r
1309 char* lastIndirectionFilename;\r
1310 ArgDescriptor argDescriptorIndirection =\r
1311 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1312 \r
1313 \r
1314 VOID\r
1315 ExitArgError(char *msg, char *badArg)\r
1316 {\r
1317   char buf[MSG_SIZ];\r
1318 \r
1319   sprintf(buf, "%s %s", msg, badArg);\r
1320   DisplayFatalError(buf, 0, 2);\r
1321   exit(2);\r
1322 }\r
1323 \r
1324 /* Command line font name parser.  NULL name means do nothing.\r
1325    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1326    For backward compatibility, syntax without the colon is also\r
1327    accepted, but font names with digits in them won't work in that case.\r
1328 */\r
1329 VOID\r
1330 ParseFontName(char *name, MyFontParams *mfp)\r
1331 {\r
1332   char *p, *q;\r
1333   if (name == NULL) return;\r
1334   p = name;\r
1335   q = strchr(p, ':');\r
1336   if (q) {\r
1337     if (q - p >= sizeof(mfp->faceName))\r
1338       ExitArgError("Font name too long:", name);\r
1339     memcpy(mfp->faceName, p, q - p);\r
1340     mfp->faceName[q - p] = NULLCHAR;\r
1341     p = q + 1;\r
1342   } else {\r
1343     q = mfp->faceName;\r
1344     while (*p && !isdigit(*p)) {\r
1345       *q++ = *p++;\r
1346       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1347         ExitArgError("Font name too long:", name);\r
1348     }\r
1349     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1350     *q = NULLCHAR;\r
1351   }\r
1352   if (!*p) ExitArgError("Font point size missing:", name);\r
1353   mfp->pointSize = (float) atof(p);\r
1354   mfp->bold = (strchr(p, 'b') != NULL);\r
1355   mfp->italic = (strchr(p, 'i') != NULL);\r
1356   mfp->underline = (strchr(p, 'u') != NULL);\r
1357   mfp->strikeout = (strchr(p, 's') != NULL);\r
1358 }\r
1359 \r
1360 /* Color name parser.\r
1361    X version accepts X color names, but this one\r
1362    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1363 COLORREF\r
1364 ParseColorName(char *name)\r
1365 {\r
1366   int red, green, blue, count;\r
1367   char buf[MSG_SIZ];\r
1368 \r
1369   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1370   if (count != 3) {\r
1371     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1372       &red, &green, &blue);\r
1373   }\r
1374   if (count != 3) {\r
1375     sprintf(buf, "Can't parse color name %s", name);\r
1376     DisplayError(buf, 0);\r
1377     return RGB(0, 0, 0);\r
1378   }\r
1379   return PALETTERGB(red, green, blue);\r
1380 }\r
1381 \r
1382 \r
1383 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1384 {\r
1385   char *e = argValue;\r
1386   int eff = 0;\r
1387 \r
1388   while (*e) {\r
1389     if (*e == 'b')      eff |= CFE_BOLD;\r
1390     else if (*e == 'i') eff |= CFE_ITALIC;\r
1391     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1392     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1393     else if (*e == '#' || isdigit(*e)) break;\r
1394     e++;\r
1395   }\r
1396   *effects = eff;\r
1397   *color   = ParseColorName(e);\r
1398 }\r
1399 \r
1400 \r
1401 BoardSize\r
1402 ParseBoardSize(char *name)\r
1403 {\r
1404   BoardSize bs = SizeTiny;\r
1405   while (sizeInfo[bs].name != NULL) {\r
1406     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1407     bs++;\r
1408   }\r
1409   ExitArgError("Unrecognized board size value", name);\r
1410   return bs; /* not reached */\r
1411 }\r
1412 \r
1413 \r
1414 char\r
1415 StringGet(void *getClosure)\r
1416 {\r
1417   char **p = (char **) getClosure;\r
1418   return *((*p)++);\r
1419 }\r
1420 \r
1421 char\r
1422 FileGet(void *getClosure)\r
1423 {\r
1424   int c;\r
1425   FILE* f = (FILE*) getClosure;\r
1426 \r
1427   c = getc(f);\r
1428   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1429   if (c == EOF)\r
1430     return NULLCHAR;\r
1431   else\r
1432     return (char) c;\r
1433 }\r
1434 \r
1435 /* Parse settings file named "name". If file found, return the\r
1436    full name in fullname and return TRUE; else return FALSE */\r
1437 BOOLEAN\r
1438 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1439 {\r
1440   char *dummy;\r
1441   FILE *f;\r
1442   int ok; char buf[MSG_SIZ];\r
1443 \r
1444   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1445   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1446     sprintf(buf, "%s.ini", name);\r
1447     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1448   }\r
1449   if (ok) {\r
1450     f = fopen(fullname, "r");\r
1451     if (f != NULL) {\r
1452       ParseArgs(FileGet, f);\r
1453       fclose(f);\r
1454       return TRUE;\r
1455     }\r
1456   }\r
1457   return FALSE;\r
1458 }\r
1459 \r
1460 VOID\r
1461 ParseArgs(GetFunc get, void *cl)\r
1462 {\r
1463   char argName[ARG_MAX];\r
1464   char argValue[ARG_MAX];\r
1465   ArgDescriptor *ad;\r
1466   char start;\r
1467   char *q;\r
1468   int i, octval;\r
1469   char ch;\r
1470   int posarg = 0;\r
1471 \r
1472   ch = get(cl);\r
1473   for (;;) {\r
1474     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1475     if (ch == NULLCHAR) break;\r
1476     if (ch == ';') {\r
1477       /* Comment to end of line */\r
1478       ch = get(cl);\r
1479       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1480       continue;\r
1481     } else if (ch == '/' || ch == '-') {\r
1482       /* Switch */\r
1483       q = argName;\r
1484       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1485              ch != '\n' && ch != '\t') {\r
1486         *q++ = ch;\r
1487         ch = get(cl);\r
1488       }\r
1489       *q = NULLCHAR;\r
1490 \r
1491       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1492         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1493 \r
1494       if (ad->argName == NULL)\r
1495         ExitArgError("Unrecognized argument", argName);\r
1496 \r
1497     } else if (ch == '@') {\r
1498       /* Indirection file */\r
1499       ad = &argDescriptorIndirection;\r
1500       ch = get(cl);\r
1501     } else {\r
1502       /* Positional argument */\r
1503       ad = &argDescriptors[posarg++];\r
1504       strcpy(argName, ad->argName);\r
1505     }\r
1506 \r
1507     if (ad->argType == ArgTrue) {\r
1508       *(Boolean *) ad->argLoc = TRUE;\r
1509       continue;\r
1510     }\r
1511     if (ad->argType == ArgFalse) {\r
1512       *(Boolean *) ad->argLoc = FALSE;\r
1513       continue;\r
1514     }\r
1515 \r
1516     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1517     if (ch == NULLCHAR || ch == '\n') {\r
1518       ExitArgError("No value provided for argument", argName);\r
1519     }\r
1520     q = argValue;\r
1521     if (ch == '{') {\r
1522       // Quoting with { }.  No characters have to (or can) be escaped.\r
1523       // Thus the string cannot contain a '}' character.\r
1524       start = ch;\r
1525       ch = get(cl);\r
1526       while (start) {\r
1527         switch (ch) {\r
1528         case NULLCHAR:\r
1529           start = NULLCHAR;\r
1530           break;\r
1531           \r
1532         case '}':\r
1533           ch = get(cl);\r
1534           start = NULLCHAR;\r
1535           break;\r
1536 \r
1537         default:\r
1538           *q++ = ch;\r
1539           ch = get(cl);\r
1540           break;\r
1541         }\r
1542       }   \r
1543     } else if (ch == '\'' || ch == '"') {\r
1544       // Quoting with ' ' or " ", with \ as escape character.\r
1545       // Inconvenient for long strings that may contain Windows filenames.\r
1546       start = ch;\r
1547       ch = get(cl);\r
1548       while (start) {\r
1549         switch (ch) {\r
1550         case NULLCHAR:\r
1551           start = NULLCHAR;\r
1552           break;\r
1553 \r
1554         default:\r
1555         not_special:\r
1556           *q++ = ch;\r
1557           ch = get(cl);\r
1558           break;\r
1559 \r
1560         case '\'':\r
1561         case '\"':\r
1562           if (ch == start) {\r
1563             ch = get(cl);\r
1564             start = NULLCHAR;\r
1565             break;\r
1566           } else {\r
1567             goto not_special;\r
1568           }\r
1569 \r
1570         case '\\':\r
1571           if (ad->argType == ArgFilename\r
1572               || ad->argType == ArgSettingsFilename) {\r
1573               goto not_special;\r
1574           }\r
1575           ch = get(cl);\r
1576           switch (ch) {\r
1577           case NULLCHAR:\r
1578             ExitArgError("Incomplete \\ escape in value for", argName);\r
1579             break;\r
1580           case 'n':\r
1581             *q++ = '\n';\r
1582             ch = get(cl);\r
1583             break;\r
1584           case 'r':\r
1585             *q++ = '\r';\r
1586             ch = get(cl);\r
1587             break;\r
1588           case 't':\r
1589             *q++ = '\t';\r
1590             ch = get(cl);\r
1591             break;\r
1592           case 'b':\r
1593             *q++ = '\b';\r
1594             ch = get(cl);\r
1595             break;\r
1596           case 'f':\r
1597             *q++ = '\f';\r
1598             ch = get(cl);\r
1599             break;\r
1600           default:\r
1601             octval = 0;\r
1602             for (i = 0; i < 3; i++) {\r
1603               if (ch >= '0' && ch <= '7') {\r
1604                 octval = octval*8 + (ch - '0');\r
1605                 ch = get(cl);\r
1606               } else {\r
1607                 break;\r
1608               }\r
1609             }\r
1610             if (i > 0) {\r
1611               *q++ = (char) octval;\r
1612             } else {\r
1613               *q++ = ch;\r
1614               ch = get(cl);\r
1615             }\r
1616             break;\r
1617           }\r
1618           break;\r
1619         }\r
1620       }\r
1621     } else {\r
1622       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1623         *q++ = ch;\r
1624         ch = get(cl);\r
1625       }\r
1626     }\r
1627     *q = NULLCHAR;\r
1628 \r
1629     switch (ad->argType) {\r
1630     case ArgInt:\r
1631       *(int *) ad->argLoc = atoi(argValue);\r
1632       break;\r
1633 \r
1634     case ArgFloat:\r
1635       *(float *) ad->argLoc = (float) atof(argValue);\r
1636       break;\r
1637 \r
1638     case ArgString:\r
1639     case ArgFilename:\r
1640       *(char **) ad->argLoc = strdup(argValue);\r
1641       break;\r
1642 \r
1643     case ArgSettingsFilename:\r
1644       {\r
1645         char fullname[MSG_SIZ];\r
1646         if (ParseSettingsFile(argValue, fullname)) {\r
1647           if (ad->argLoc != NULL) {\r
1648             *(char **) ad->argLoc = strdup(fullname);\r
1649           }\r
1650         } else {\r
1651           if (ad->argLoc != NULL) {\r
1652           } else {\r
1653             ExitArgError("Failed to open indirection file", argValue);\r
1654           }\r
1655         }\r
1656       }\r
1657       break;\r
1658 \r
1659     case ArgBoolean:\r
1660       switch (argValue[0]) {\r
1661       case 't':\r
1662       case 'T':\r
1663         *(Boolean *) ad->argLoc = TRUE;\r
1664         break;\r
1665       case 'f':\r
1666       case 'F':\r
1667         *(Boolean *) ad->argLoc = FALSE;\r
1668         break;\r
1669       default:\r
1670         ExitArgError("Unrecognized boolean argument value", argValue);\r
1671         break;\r
1672       }\r
1673       break;\r
1674 \r
1675     case ArgColor:\r
1676       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1677       break;\r
1678 \r
1679     case ArgAttribs: {\r
1680       ColorClass cc = (ColorClass)ad->argLoc;\r
1681       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1682       }\r
1683       break;\r
1684       \r
1685     case ArgBoardSize:\r
1686       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1687       break;\r
1688 \r
1689     case ArgFont:\r
1690       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1691       break;\r
1692 \r
1693     case ArgCommSettings:\r
1694       ParseCommSettings(argValue, &dcb);\r
1695       break;\r
1696 \r
1697     case ArgNone:\r
1698       ExitArgError("Unrecognized argument", argValue);\r
1699       break;\r
1700     case ArgTrue:\r
1701     case ArgFalse: ;\r
1702     }\r
1703   }\r
1704 }\r
1705 \r
1706 VOID\r
1707 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1708 {\r
1709   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1710   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1711   DeleteDC(hdc);\r
1712   lf->lfWidth = 0;\r
1713   lf->lfEscapement = 0;\r
1714   lf->lfOrientation = 0;\r
1715   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1716   lf->lfItalic = mfp->italic;\r
1717   lf->lfUnderline = mfp->underline;\r
1718   lf->lfStrikeOut = mfp->strikeout;\r
1719   lf->lfCharSet = DEFAULT_CHARSET;\r
1720   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1721   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1722   lf->lfQuality = DEFAULT_QUALITY;\r
1723   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1724   strcpy(lf->lfFaceName, mfp->faceName);\r
1725 }\r
1726 \r
1727 VOID\r
1728 CreateFontInMF(MyFont *mf)\r
1729 {\r
1730   LFfromMFP(&mf->lf, &mf->mfp);\r
1731   if (mf->hf) DeleteObject(mf->hf);\r
1732   mf->hf = CreateFontIndirect(&mf->lf);\r
1733 }\r
1734 \r
1735 VOID\r
1736 SetDefaultTextAttribs()\r
1737 {\r
1738   ColorClass cc;\r
1739   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1740     ParseAttribs(&textAttribs[cc].color, \r
1741                  &textAttribs[cc].effects, \r
1742                  defaultTextAttribs[cc]);\r
1743   }\r
1744 }\r
1745 \r
1746 VOID\r
1747 SetDefaultSounds()\r
1748 {\r
1749   ColorClass cc;\r
1750   SoundClass sc;\r
1751   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1752     textAttribs[cc].sound.name = strdup("");\r
1753     textAttribs[cc].sound.data = NULL;\r
1754   }\r
1755   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1756     sounds[sc].name = strdup("");\r
1757     sounds[sc].data = NULL;\r
1758   }\r
1759   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1760 }\r
1761 \r
1762 VOID\r
1763 LoadAllSounds()\r
1764 {\r
1765   ColorClass cc;\r
1766   SoundClass sc;\r
1767   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1768     MyLoadSound(&textAttribs[cc].sound);\r
1769   }\r
1770   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1771     MyLoadSound(&sounds[sc]);\r
1772   }\r
1773 }\r
1774 \r
1775 VOID\r
1776 InitAppData(LPSTR lpCmdLine)\r
1777 {\r
1778   int i, j;\r
1779   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1780   char *dummy, *p;\r
1781 \r
1782   programName = szAppName;\r
1783 \r
1784   /* Initialize to defaults */\r
1785   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1786   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1787   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1788   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1789   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1790   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1791   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1792   SetDefaultTextAttribs();\r
1793   SetDefaultSounds();\r
1794   appData.movesPerSession = MOVES_PER_SESSION;\r
1795   appData.initString = INIT_STRING;\r
1796   appData.secondInitString = INIT_STRING;\r
1797   appData.firstComputerString = COMPUTER_STRING;\r
1798   appData.secondComputerString = COMPUTER_STRING;\r
1799   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1800   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1801   appData.firstPlaysBlack = FALSE;\r
1802   appData.noChessProgram = FALSE;\r
1803   chessProgram = FALSE;\r
1804   appData.firstHost = FIRST_HOST;\r
1805   appData.secondHost = SECOND_HOST;\r
1806   appData.firstDirectory = FIRST_DIRECTORY;\r
1807   appData.secondDirectory = SECOND_DIRECTORY;\r
1808   appData.bitmapDirectory = "";\r
1809   appData.remoteShell = REMOTE_SHELL;\r
1810   appData.remoteUser = "";\r
1811   appData.timeDelay = TIME_DELAY;\r
1812   appData.timeControl = TIME_CONTROL;\r
1813   appData.timeIncrement = TIME_INCREMENT;\r
1814   appData.icsActive = FALSE;\r
1815   appData.icsHost = "";\r
1816   appData.icsPort = ICS_PORT;\r
1817   appData.icsCommPort = ICS_COMM_PORT;\r
1818   appData.icsLogon = ICS_LOGON;\r
1819   appData.icsHelper = "";\r
1820   appData.useTelnet = FALSE;\r
1821   appData.telnetProgram = TELNET_PROGRAM;\r
1822   appData.gateway = "";\r
1823   appData.loadGameFile = "";\r
1824   appData.loadGameIndex = 0;\r
1825   appData.saveGameFile = "";\r
1826   appData.autoSaveGames = FALSE;\r
1827   appData.loadPositionFile = "";\r
1828   appData.loadPositionIndex = 1;\r
1829   appData.savePositionFile = "";\r
1830   appData.matchMode = FALSE;\r
1831   appData.matchGames = 0;\r
1832   appData.monoMode = FALSE;\r
1833   appData.debugMode = FALSE;\r
1834   appData.clockMode = TRUE;\r
1835   boardSize = (BoardSize) -1; /* determine by screen size */\r
1836   appData.Iconic = FALSE; /*unused*/\r
1837   appData.searchTime = "";\r
1838   appData.searchDepth = 0;\r
1839   appData.showCoords = FALSE;\r
1840   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1841   appData.autoCallFlag = FALSE;\r
1842   appData.flipView = FALSE;\r
1843   appData.autoFlipView = TRUE;\r
1844   appData.cmailGameName = "";\r
1845   appData.alwaysPromoteToQueen = FALSE;\r
1846   appData.oldSaveStyle = FALSE;\r
1847   appData.quietPlay = FALSE;\r
1848   appData.showThinking = FALSE;\r
1849   appData.ponderNextMove = TRUE;\r
1850   appData.periodicUpdates = TRUE;\r
1851   appData.popupExitMessage = TRUE;\r
1852   appData.popupMoveErrors = FALSE;\r
1853   appData.autoObserve = FALSE;\r
1854   appData.autoComment = FALSE;\r
1855   appData.animate = TRUE;\r
1856   appData.animSpeed = 10;\r
1857   appData.animateDragging = TRUE;\r
1858   appData.highlightLastMove = TRUE;\r
1859   appData.getMoveList = TRUE;\r
1860   appData.testLegality = TRUE;\r
1861   appData.premove = TRUE;\r
1862   appData.premoveWhite = FALSE;\r
1863   appData.premoveWhiteText = "";\r
1864   appData.premoveBlack = FALSE;\r
1865   appData.premoveBlackText = "";\r
1866   appData.icsAlarm = TRUE;\r
1867   appData.icsAlarmTime = 5000;\r
1868   appData.autoRaiseBoard = TRUE;\r
1869   appData.localLineEditing = TRUE;\r
1870   appData.colorize = TRUE;\r
1871   appData.reuseFirst = TRUE;\r
1872   appData.reuseSecond = TRUE;\r
1873   appData.blindfold = FALSE;\r
1874   appData.icsEngineAnalyze = FALSE;\r
1875   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1876   dcb.DCBlength = sizeof(DCB);\r
1877   dcb.BaudRate = 9600;\r
1878   dcb.fBinary = TRUE;\r
1879   dcb.fParity = FALSE;\r
1880   dcb.fOutxCtsFlow = FALSE;\r
1881   dcb.fOutxDsrFlow = FALSE;\r
1882   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1883   dcb.fDsrSensitivity = FALSE;\r
1884   dcb.fTXContinueOnXoff = TRUE;\r
1885   dcb.fOutX = FALSE;\r
1886   dcb.fInX = FALSE;\r
1887   dcb.fNull = FALSE;\r
1888   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1889   dcb.fAbortOnError = FALSE;\r
1890   dcb.ByteSize = 7;\r
1891   dcb.Parity = SPACEPARITY;\r
1892   dcb.StopBits = ONESTOPBIT;\r
1893   settingsFileName = SETTINGS_FILE;\r
1894   saveSettingsOnExit = TRUE;\r
1895   boardX = CW_USEDEFAULT;\r
1896   boardY = CW_USEDEFAULT;\r
1897   consoleX = CW_USEDEFAULT; \r
1898   consoleY = CW_USEDEFAULT; \r
1899   consoleW = CW_USEDEFAULT;\r
1900   consoleH = CW_USEDEFAULT;\r
1901   analysisX = CW_USEDEFAULT; \r
1902   analysisY = CW_USEDEFAULT; \r
1903   analysisW = CW_USEDEFAULT;\r
1904   analysisH = CW_USEDEFAULT;\r
1905   commentX = CW_USEDEFAULT; \r
1906   commentY = CW_USEDEFAULT; \r
1907   commentW = CW_USEDEFAULT;\r
1908   commentH = CW_USEDEFAULT;\r
1909   editTagsX = CW_USEDEFAULT; \r
1910   editTagsY = CW_USEDEFAULT; \r
1911   editTagsW = CW_USEDEFAULT;\r
1912   editTagsH = CW_USEDEFAULT;\r
1913   gameListX = CW_USEDEFAULT; \r
1914   gameListY = CW_USEDEFAULT; \r
1915   gameListW = CW_USEDEFAULT;\r
1916   gameListH = CW_USEDEFAULT;\r
1917   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1918   icsNames = ICS_NAMES;\r
1919   firstChessProgramNames = FCP_NAMES;\r
1920   secondChessProgramNames = SCP_NAMES;\r
1921   appData.initialMode = "";\r
1922   appData.variant = "normal";\r
1923   appData.firstProtocolVersion = PROTOVER;\r
1924   appData.secondProtocolVersion = PROTOVER;\r
1925   appData.showButtonBar = TRUE;\r
1926 \r
1927    /* [AS] New properties (see comments in header file) */\r
1928   appData.firstScoreIsAbsolute = FALSE;\r
1929   appData.secondScoreIsAbsolute = FALSE;\r
1930   appData.saveExtendedInfoInPGN = FALSE;\r
1931   appData.hideThinkingFromHuman = FALSE;\r
1932   appData.liteBackTextureFile = "";\r
1933   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1934   appData.darkBackTextureFile = "";\r
1935   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1936   appData.renderPiecesWithFont = "";\r
1937   appData.fontToPieceTable = "";\r
1938   appData.fontBackColorWhite = 0;\r
1939   appData.fontForeColorWhite = 0;\r
1940   appData.fontBackColorBlack = 0;\r
1941   appData.fontForeColorBlack = 0;\r
1942   appData.fontPieceSize = 80;\r
1943   appData.overrideLineGap = 1;\r
1944   appData.adjudicateLossThreshold = 0;\r
1945   appData.delayBeforeQuit = 0;\r
1946   appData.delayAfterQuit = 0;\r
1947   appData.nameOfDebugFile = "winboard.debug";\r
1948   appData.pgnEventHeader = "Computer Chess Game";\r
1949   appData.defaultFrcPosition = -1;\r
1950   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1951   appData.saveOutOfBookInfo = TRUE;\r
1952   appData.showEvalInMoveHistory = TRUE;\r
1953   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1954   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1955   appData.highlightMoveWithArrow = FALSE;\r
1956   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1957   appData.useStickyWindows = TRUE;\r
1958   appData.adjudicateDrawMoves = 0;\r
1959   appData.autoDisplayComment = TRUE;\r
1960   appData.autoDisplayTags = TRUE;\r
1961   appData.firstIsUCI = FALSE;\r
1962   appData.secondIsUCI = FALSE;\r
1963   appData.firstHasOwnBookUCI = TRUE;\r
1964   appData.secondHasOwnBookUCI = TRUE;\r
1965   appData.polyglotDir = "";\r
1966   appData.usePolyglotBook = FALSE;\r
1967   appData.polyglotBook = "";\r
1968   appData.defaultHashSize = 64;\r
1969   appData.defaultCacheSizeEGTB = 4;\r
1970   appData.defaultPathEGTB = "c:\\egtb";\r
1971   appData.firstOptions = "";\r
1972   appData.secondOptions = "";\r
1973 \r
1974   InitWindowPlacement( &wpMoveHistory );\r
1975   InitWindowPlacement( &wpEvalGraph );\r
1976   InitWindowPlacement( &wpEngineOutput );\r
1977 \r
1978   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1979   appData.NrFiles      = -1;\r
1980   appData.NrRanks      = -1;\r
1981   appData.holdingsSize = -1;\r
1982   appData.testClaims   = FALSE;\r
1983   appData.checkMates   = FALSE;\r
1984   appData.materialDraws= FALSE;\r
1985   appData.trivialDraws = FALSE;\r
1986   appData.ruleMoves    = 51;\r
1987   appData.drawRepeats  = 6;\r
1988   appData.matchPause   = 10000;\r
1989   appData.alphaRank    = FALSE;\r
1990   appData.allWhite     = FALSE;\r
1991   appData.upsideDown   = FALSE;\r
1992   appData.serverPause  = 15;\r
1993   appData.serverMovesName   = NULL;\r
1994   appData.suppressLoadMoves = FALSE;\r
1995   appData.firstTimeOdds  = 1;\r
1996   appData.secondTimeOdds = 1;\r
1997   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1998   appData.secondAccumulateTC = 1;\r
1999   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2000   appData.secondNPS = -1;\r
2001   appData.engineComments = 1;\r
2002   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2003   appData.egtFormats = "";\r
2004 \r
2005 #ifdef ZIPPY\r
2006   appData.zippyTalk = ZIPPY_TALK;\r
2007   appData.zippyPlay = ZIPPY_PLAY;\r
2008   appData.zippyLines = ZIPPY_LINES;\r
2009   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2010   appData.zippyPassword = ZIPPY_PASSWORD;\r
2011   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2012   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2013   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2014   appData.zippyUseI = ZIPPY_USE_I;\r
2015   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2016   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2017   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2018   appData.zippyGameStart = ZIPPY_GAME_START;\r
2019   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2020   appData.zippyAbort = ZIPPY_ABORT;\r
2021   appData.zippyVariants = ZIPPY_VARIANTS;\r
2022   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2023   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2024 #endif\r
2025 \r
2026   /* Point font array elements to structures and\r
2027      parse default font names */\r
2028   for (i=0; i<NUM_FONTS; i++) {\r
2029     for (j=0; j<NUM_SIZES; j++) {\r
2030       font[j][i] = &fontRec[j][i];\r
2031       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2032     }\r
2033   }\r
2034   \r
2035   /* Parse default settings file if any */\r
2036   if (ParseSettingsFile(settingsFileName, buf)) {\r
2037     settingsFileName = strdup(buf);\r
2038   }\r
2039 \r
2040   /* Parse command line */\r
2041   ParseArgs(StringGet, &lpCmdLine);\r
2042 \r
2043   /* [HGM] make sure board size is acceptable */\r
2044   if(appData.NrFiles > BOARD_SIZE ||\r
2045      appData.NrRanks > BOARD_SIZE   )\r
2046       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2047 \r
2048   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2049    * with options from the command line, we now make an even higher priority\r
2050    * overrule by WB options attached to the engine command line. This so that\r
2051    * tournament managers can use WB options (such as /timeOdds) that follow\r
2052    * the engines.\r
2053    */\r
2054   if(appData.firstChessProgram != NULL) {\r
2055       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2056       static char *f = "first";\r
2057       char buf[MSG_SIZ], *q = buf;\r
2058       if(p != NULL) { // engine command line contains WinBoard options\r
2059           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2060           ParseArgs(StringGet, &q);\r
2061           p[-1] = 0; // cut them offengine command line\r
2062       }\r
2063   }\r
2064   // now do same for second chess program\r
2065   if(appData.secondChessProgram != NULL) {\r
2066       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2067       static char *s = "second";\r
2068       char buf[MSG_SIZ], *q = buf;\r
2069       if(p != NULL) { // engine command line contains WinBoard options\r
2070           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2071           ParseArgs(StringGet, &q);\r
2072           p[-1] = 0; // cut them offengine command line\r
2073       }\r
2074   }\r
2075 \r
2076 \r
2077   /* Propagate options that affect others */\r
2078   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2079   if (appData.icsActive || appData.noChessProgram) {\r
2080      chessProgram = FALSE;  /* not local chess program mode */\r
2081   }\r
2082 \r
2083   /* Open startup dialog if needed */\r
2084   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2085       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2086       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2087                         *appData.secondChessProgram == NULLCHAR))) {\r
2088     FARPROC lpProc;\r
2089     \r
2090     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2091     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2092     FreeProcInstance(lpProc);\r
2093   }\r
2094 \r
2095   /* Make sure save files land in the right (?) directory */\r
2096   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2097     appData.saveGameFile = strdup(buf);\r
2098   }\r
2099   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2100     appData.savePositionFile = strdup(buf);\r
2101   }\r
2102 \r
2103   /* Finish initialization for fonts and sounds */\r
2104   for (i=0; i<NUM_FONTS; i++) {\r
2105     for (j=0; j<NUM_SIZES; j++) {\r
2106       CreateFontInMF(font[j][i]);\r
2107     }\r
2108   }\r
2109   /* xboard, and older WinBoards, controlled the move sound with the\r
2110      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2111      always turn the option on (so that the backend will call us),\r
2112      then let the user turn the sound off by setting it to silence if\r
2113      desired.  To accommodate old winboard.ini files saved by old\r
2114      versions of WinBoard, we also turn off the sound if the option\r
2115      was initially set to false. */\r
2116   if (!appData.ringBellAfterMoves) {\r
2117     sounds[(int)SoundMove].name = strdup("");\r
2118     appData.ringBellAfterMoves = TRUE;\r
2119   }\r
2120   GetCurrentDirectory(MSG_SIZ, currDir);\r
2121   SetCurrentDirectory(installDir);\r
2122   LoadAllSounds();\r
2123   SetCurrentDirectory(currDir);\r
2124 \r
2125   p = icsTextMenuString;\r
2126   if (p[0] == '@') {\r
2127     FILE* f = fopen(p + 1, "r");\r
2128     if (f == NULL) {\r
2129       DisplayFatalError(p + 1, errno, 2);\r
2130       return;\r
2131     }\r
2132     i = fread(buf, 1, sizeof(buf)-1, f);\r
2133     fclose(f);\r
2134     buf[i] = NULLCHAR;\r
2135     p = buf;\r
2136   }\r
2137   ParseIcsTextMenu(strdup(p));\r
2138 }\r
2139 \r
2140 \r
2141 VOID\r
2142 InitMenuChecks()\r
2143 {\r
2144   HMENU hmenu = GetMenu(hwndMain);\r
2145 \r
2146   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2147                         MF_BYCOMMAND|((appData.icsActive &&\r
2148                                        *appData.icsCommPort != NULLCHAR) ?\r
2149                                       MF_ENABLED : MF_GRAYED));\r
2150   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2151                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2152                                      MF_CHECKED : MF_UNCHECKED));\r
2153 }\r
2154 \r
2155 \r
2156 VOID\r
2157 SaveSettings(char* name)\r
2158 {\r
2159   FILE *f;\r
2160   ArgDescriptor *ad;\r
2161   WINDOWPLACEMENT wp;\r
2162   char dir[MSG_SIZ];\r
2163 \r
2164   if (!hwndMain) return;\r
2165 \r
2166   GetCurrentDirectory(MSG_SIZ, dir);\r
2167   SetCurrentDirectory(installDir);\r
2168   f = fopen(name, "w");\r
2169   SetCurrentDirectory(dir);\r
2170   if (f == NULL) {\r
2171     DisplayError(name, errno);\r
2172     return;\r
2173   }\r
2174   fprintf(f, ";\n");\r
2175   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2176   fprintf(f, ";\n");\r
2177   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2178   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2179   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2180   fprintf(f, ";\n");\r
2181 \r
2182   wp.length = sizeof(WINDOWPLACEMENT);\r
2183   GetWindowPlacement(hwndMain, &wp);\r
2184   boardX = wp.rcNormalPosition.left;\r
2185   boardY = wp.rcNormalPosition.top;\r
2186 \r
2187   if (hwndConsole) {\r
2188     GetWindowPlacement(hwndConsole, &wp);\r
2189     consoleX = wp.rcNormalPosition.left;\r
2190     consoleY = wp.rcNormalPosition.top;\r
2191     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2192     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2193   }\r
2194 \r
2195   if (analysisDialog) {\r
2196     GetWindowPlacement(analysisDialog, &wp);\r
2197     analysisX = wp.rcNormalPosition.left;\r
2198     analysisY = wp.rcNormalPosition.top;\r
2199     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2200     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2201   }\r
2202 \r
2203   if (commentDialog) {\r
2204     GetWindowPlacement(commentDialog, &wp);\r
2205     commentX = wp.rcNormalPosition.left;\r
2206     commentY = wp.rcNormalPosition.top;\r
2207     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2208     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2209   }\r
2210 \r
2211   if (editTagsDialog) {\r
2212     GetWindowPlacement(editTagsDialog, &wp);\r
2213     editTagsX = wp.rcNormalPosition.left;\r
2214     editTagsY = wp.rcNormalPosition.top;\r
2215     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2216     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2217   }\r
2218 \r
2219   if (gameListDialog) {\r
2220     GetWindowPlacement(gameListDialog, &wp);\r
2221     gameListX = wp.rcNormalPosition.left;\r
2222     gameListY = wp.rcNormalPosition.top;\r
2223     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2224     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2225   }\r
2226 \r
2227   /* [AS] Move history */\r
2228   wpMoveHistory.visible = MoveHistoryIsUp();\r
2229   \r
2230   if( moveHistoryDialog ) {\r
2231     GetWindowPlacement(moveHistoryDialog, &wp);\r
2232     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2233     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2234     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2235     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2236   }\r
2237 \r
2238   /* [AS] Eval graph */\r
2239   wpEvalGraph.visible = EvalGraphIsUp();\r
2240 \r
2241   if( evalGraphDialog ) {\r
2242     GetWindowPlacement(evalGraphDialog, &wp);\r
2243     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2244     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2245     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2246     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2247   }\r
2248 \r
2249   /* [AS] Engine output */\r
2250   wpEngineOutput.visible = EngineOutputIsUp();\r
2251 \r
2252   if( engineOutputDialog ) {\r
2253     GetWindowPlacement(engineOutputDialog, &wp);\r
2254     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2255     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2256     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2257     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2258   }\r
2259 \r
2260   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2261     if (!ad->save) continue;\r
2262     switch (ad->argType) {\r
2263     case ArgString:\r
2264       {\r
2265         char *p = *(char **)ad->argLoc;\r
2266         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2267           /* Quote multiline values or \-containing values\r
2268              with { } if possible */\r
2269           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2270         } else {\r
2271           /* Else quote with " " */\r
2272           fprintf(f, "/%s=\"", ad->argName);\r
2273           while (*p) {\r
2274             if (*p == '\n') fprintf(f, "\n");\r
2275             else if (*p == '\r') fprintf(f, "\\r");\r
2276             else if (*p == '\t') fprintf(f, "\\t");\r
2277             else if (*p == '\b') fprintf(f, "\\b");\r
2278             else if (*p == '\f') fprintf(f, "\\f");\r
2279             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2280             else if (*p == '\"') fprintf(f, "\\\"");\r
2281             else if (*p == '\\') fprintf(f, "\\\\");\r
2282             else putc(*p, f);\r
2283             p++;\r
2284           }\r
2285           fprintf(f, "\"\n");\r
2286         }\r
2287       }\r
2288       break;\r
2289     case ArgInt:\r
2290       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2291       break;\r
2292     case ArgFloat:\r
2293       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2294       break;\r
2295     case ArgBoolean:\r
2296       fprintf(f, "/%s=%s\n", ad->argName, \r
2297         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2298       break;\r
2299     case ArgTrue:\r
2300       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2301       break;\r
2302     case ArgFalse:\r
2303       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2304       break;\r
2305     case ArgColor:\r
2306       {\r
2307         COLORREF color = *(COLORREF *)ad->argLoc;\r
2308         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2309           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2310       }\r
2311       break;\r
2312     case ArgAttribs:\r
2313       {\r
2314         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2315         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2316           (ta->effects & CFE_BOLD) ? "b" : "",\r
2317           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2318           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2319           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2320           (ta->effects) ? " " : "",\r
2321           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2322       }\r
2323       break;\r
2324     case ArgFilename:\r
2325       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2326         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2327       } else {\r
2328         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2329       }\r
2330       break;\r
2331     case ArgBoardSize:\r
2332       fprintf(f, "/%s=%s\n", ad->argName,\r
2333               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2334       break;\r
2335     case ArgFont:\r
2336       {\r
2337         int bs;\r
2338         for (bs=0; bs<NUM_SIZES; bs++) {\r
2339           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2340           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2341           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2342             ad->argName, mfp->faceName, mfp->pointSize,\r
2343             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2344             mfp->bold ? "b" : "",\r
2345             mfp->italic ? "i" : "",\r
2346             mfp->underline ? "u" : "",\r
2347             mfp->strikeout ? "s" : "");\r
2348         }\r
2349       }\r
2350       break;\r
2351     case ArgCommSettings:\r
2352       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2353     case ArgNone:\r
2354     case ArgSettingsFilename: ;\r
2355     }\r
2356   }\r
2357   fclose(f);\r
2358 }\r
2359 \r
2360 \r
2361 \r
2362 /*---------------------------------------------------------------------------*\\r
2363  *\r
2364  * GDI board drawing routines\r
2365  *\r
2366 \*---------------------------------------------------------------------------*/\r
2367 \r
2368 /* [AS] Draw square using background texture */\r
2369 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2370 {\r
2371     XFORM   x;\r
2372 \r
2373     if( mode == 0 ) {\r
2374         return; /* Should never happen! */\r
2375     }\r
2376 \r
2377     SetGraphicsMode( dst, GM_ADVANCED );\r
2378 \r
2379     switch( mode ) {\r
2380     case 1:\r
2381         /* Identity */\r
2382         break;\r
2383     case 2:\r
2384         /* X reflection */\r
2385         x.eM11 = -1.0;\r
2386         x.eM12 = 0;\r
2387         x.eM21 = 0;\r
2388         x.eM22 = 1.0;\r
2389         x.eDx = (FLOAT) dw + dx - 1;\r
2390         x.eDy = 0;\r
2391         dx = 0;\r
2392         SetWorldTransform( dst, &x );\r
2393         break;\r
2394     case 3:\r
2395         /* Y reflection */\r
2396         x.eM11 = 1.0;\r
2397         x.eM12 = 0;\r
2398         x.eM21 = 0;\r
2399         x.eM22 = -1.0;\r
2400         x.eDx = 0;\r
2401         x.eDy = (FLOAT) dh + dy - 1;\r
2402         dy = 0;\r
2403         SetWorldTransform( dst, &x );\r
2404         break;\r
2405     case 4:\r
2406         /* X/Y flip */\r
2407         x.eM11 = 0;\r
2408         x.eM12 = 1.0;\r
2409         x.eM21 = 1.0;\r
2410         x.eM22 = 0;\r
2411         x.eDx = (FLOAT) dx;\r
2412         x.eDy = (FLOAT) dy;\r
2413         dx = 0;\r
2414         dy = 0;\r
2415         SetWorldTransform( dst, &x );\r
2416         break;\r
2417     }\r
2418 \r
2419     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2420 \r
2421     x.eM11 = 1.0;\r
2422     x.eM12 = 0;\r
2423     x.eM21 = 0;\r
2424     x.eM22 = 1.0;\r
2425     x.eDx = 0;\r
2426     x.eDy = 0;\r
2427     SetWorldTransform( dst, &x );\r
2428 \r
2429     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2430 }\r
2431 \r
2432 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2433 enum {\r
2434     PM_WP = (int) WhitePawn, \r
2435     PM_WN = (int) WhiteKnight, \r
2436     PM_WB = (int) WhiteBishop, \r
2437     PM_WR = (int) WhiteRook, \r
2438     PM_WQ = (int) WhiteQueen, \r
2439     PM_WF = (int) WhiteFerz, \r
2440     PM_WW = (int) WhiteWazir, \r
2441     PM_WE = (int) WhiteAlfil, \r
2442     PM_WM = (int) WhiteMan, \r
2443     PM_WO = (int) WhiteCannon, \r
2444     PM_WU = (int) WhiteUnicorn, \r
2445     PM_WH = (int) WhiteNightrider, \r
2446     PM_WA = (int) WhiteAngel, \r
2447     PM_WC = (int) WhiteMarshall, \r
2448     PM_WAB = (int) WhiteCardinal, \r
2449     PM_WD = (int) WhiteDragon, \r
2450     PM_WL = (int) WhiteLance, \r
2451     PM_WS = (int) WhiteCobra, \r
2452     PM_WV = (int) WhiteFalcon, \r
2453     PM_WSG = (int) WhiteSilver, \r
2454     PM_WG = (int) WhiteGrasshopper, \r
2455     PM_WK = (int) WhiteKing,\r
2456     PM_BP = (int) BlackPawn, \r
2457     PM_BN = (int) BlackKnight, \r
2458     PM_BB = (int) BlackBishop, \r
2459     PM_BR = (int) BlackRook, \r
2460     PM_BQ = (int) BlackQueen, \r
2461     PM_BF = (int) BlackFerz, \r
2462     PM_BW = (int) BlackWazir, \r
2463     PM_BE = (int) BlackAlfil, \r
2464     PM_BM = (int) BlackMan,\r
2465     PM_BO = (int) BlackCannon, \r
2466     PM_BU = (int) BlackUnicorn, \r
2467     PM_BH = (int) BlackNightrider, \r
2468     PM_BA = (int) BlackAngel, \r
2469     PM_BC = (int) BlackMarshall, \r
2470     PM_BG = (int) BlackGrasshopper, \r
2471     PM_BAB = (int) BlackCardinal,\r
2472     PM_BD = (int) BlackDragon,\r
2473     PM_BL = (int) BlackLance,\r
2474     PM_BS = (int) BlackCobra,\r
2475     PM_BV = (int) BlackFalcon,\r
2476     PM_BSG = (int) BlackSilver,\r
2477     PM_BK = (int) BlackKing\r
2478 };\r
2479 \r
2480 static HFONT hPieceFont = NULL;\r
2481 static HBITMAP hPieceMask[(int) EmptySquare];\r
2482 static HBITMAP hPieceFace[(int) EmptySquare];\r
2483 static int fontBitmapSquareSize = 0;\r
2484 static char pieceToFontChar[(int) EmptySquare] =\r
2485                               { 'p', 'n', 'b', 'r', 'q', \r
2486                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2487                       'k', 'o', 'm', 'v', 't', 'w', \r
2488                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2489                                                               'l' };\r
2490 \r
2491 extern BOOL SetCharTable( char *table, const char * map );\r
2492 /* [HGM] moved to backend.c */\r
2493 \r
2494 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2495 {\r
2496     HBRUSH hbrush;\r
2497     BYTE r1 = GetRValue( color );\r
2498     BYTE g1 = GetGValue( color );\r
2499     BYTE b1 = GetBValue( color );\r
2500     BYTE r2 = r1 / 2;\r
2501     BYTE g2 = g1 / 2;\r
2502     BYTE b2 = b1 / 2;\r
2503     RECT rc;\r
2504 \r
2505     /* Create a uniform background first */\r
2506     hbrush = CreateSolidBrush( color );\r
2507     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2508     FillRect( hdc, &rc, hbrush );\r
2509     DeleteObject( hbrush );\r
2510     \r
2511     if( mode == 1 ) {\r
2512         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2513         int steps = squareSize / 2;\r
2514         int i;\r
2515 \r
2516         for( i=0; i<steps; i++ ) {\r
2517             BYTE r = r1 - (r1-r2) * i / steps;\r
2518             BYTE g = g1 - (g1-g2) * i / steps;\r
2519             BYTE b = b1 - (b1-b2) * i / steps;\r
2520 \r
2521             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2522             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2523             FillRect( hdc, &rc, hbrush );\r
2524             DeleteObject(hbrush);\r
2525         }\r
2526     }\r
2527     else if( mode == 2 ) {\r
2528         /* Diagonal gradient, good more or less for every piece */\r
2529         POINT triangle[3];\r
2530         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2531         HBRUSH hbrush_old;\r
2532         int steps = squareSize;\r
2533         int i;\r
2534 \r
2535         triangle[0].x = squareSize - steps;\r
2536         triangle[0].y = squareSize;\r
2537         triangle[1].x = squareSize;\r
2538         triangle[1].y = squareSize;\r
2539         triangle[2].x = squareSize;\r
2540         triangle[2].y = squareSize - steps;\r
2541 \r
2542         for( i=0; i<steps; i++ ) {\r
2543             BYTE r = r1 - (r1-r2) * i / steps;\r
2544             BYTE g = g1 - (g1-g2) * i / steps;\r
2545             BYTE b = b1 - (b1-b2) * i / steps;\r
2546 \r
2547             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2548             hbrush_old = SelectObject( hdc, hbrush );\r
2549             Polygon( hdc, triangle, 3 );\r
2550             SelectObject( hdc, hbrush_old );\r
2551             DeleteObject(hbrush);\r
2552             triangle[0].x++;\r
2553             triangle[2].y++;\r
2554         }\r
2555 \r
2556         SelectObject( hdc, hpen );\r
2557     }\r
2558 }\r
2559 \r
2560 /*\r
2561     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2562     seems to work ok. The main problem here is to find the "inside" of a chess\r
2563     piece: follow the steps as explained below.\r
2564 */\r
2565 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2566 {\r
2567     HBITMAP hbm;\r
2568     HBITMAP hbm_old;\r
2569     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2570     RECT rc;\r
2571     SIZE sz;\r
2572     POINT pt;\r
2573     int backColor = whitePieceColor; \r
2574     int foreColor = blackPieceColor;\r
2575     \r
2576     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2577         backColor = appData.fontBackColorWhite;\r
2578         foreColor = appData.fontForeColorWhite;\r
2579     }\r
2580     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2581         backColor = appData.fontBackColorBlack;\r
2582         foreColor = appData.fontForeColorBlack;\r
2583     }\r
2584 \r
2585     /* Mask */\r
2586     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2587 \r
2588     hbm_old = SelectObject( hdc, hbm );\r
2589 \r
2590     rc.left = 0;\r
2591     rc.top = 0;\r
2592     rc.right = squareSize;\r
2593     rc.bottom = squareSize;\r
2594 \r
2595     /* Step 1: background is now black */\r
2596     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2597 \r
2598     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2599 \r
2600     pt.x = (squareSize - sz.cx) / 2;\r
2601     pt.y = (squareSize - sz.cy) / 2;\r
2602 \r
2603     SetBkMode( hdc, TRANSPARENT );\r
2604     SetTextColor( hdc, chroma );\r
2605     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2606     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2607 \r
2608     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2609     /* Step 3: the area outside the piece is filled with white */\r
2610 //    FloodFill( hdc, 0, 0, chroma );\r
2611     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2612     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2613     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2614     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2615     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2616     /* \r
2617         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2618         but if the start point is not inside the piece we're lost!\r
2619         There should be a better way to do this... if we could create a region or path\r
2620         from the fill operation we would be fine for example.\r
2621     */\r
2622 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2623     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2624 \r
2625     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2626         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2627         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2628 \r
2629         SelectObject( dc2, bm2 );\r
2630         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2631         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2632         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2634         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2635 \r
2636         DeleteDC( dc2 );\r
2637         DeleteObject( bm2 );\r
2638     }\r
2639 \r
2640     SetTextColor( hdc, 0 );\r
2641     /* \r
2642         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2643         draw the piece again in black for safety.\r
2644     */\r
2645     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2646 \r
2647     SelectObject( hdc, hbm_old );\r
2648 \r
2649     if( hPieceMask[index] != NULL ) {\r
2650         DeleteObject( hPieceMask[index] );\r
2651     }\r
2652 \r
2653     hPieceMask[index] = hbm;\r
2654 \r
2655     /* Face */\r
2656     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2657 \r
2658     SelectObject( hdc, hbm );\r
2659 \r
2660     {\r
2661         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2662         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2663         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2664 \r
2665         SelectObject( dc1, hPieceMask[index] );\r
2666         SelectObject( dc2, bm2 );\r
2667         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2668         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2669         \r
2670         /* \r
2671             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2672             the piece background and deletes (makes transparent) the rest.\r
2673             Thanks to that mask, we are free to paint the background with the greates\r
2674             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2675             We use this, to make gradients and give the pieces a "roundish" look.\r
2676         */\r
2677         SetPieceBackground( hdc, backColor, 2 );\r
2678         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2679 \r
2680         DeleteDC( dc2 );\r
2681         DeleteDC( dc1 );\r
2682         DeleteObject( bm2 );\r
2683     }\r
2684 \r
2685     SetTextColor( hdc, foreColor );\r
2686     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2687 \r
2688     SelectObject( hdc, hbm_old );\r
2689 \r
2690     if( hPieceFace[index] != NULL ) {\r
2691         DeleteObject( hPieceFace[index] );\r
2692     }\r
2693 \r
2694     hPieceFace[index] = hbm;\r
2695 }\r
2696 \r
2697 static int TranslatePieceToFontPiece( int piece )\r
2698 {\r
2699     switch( piece ) {\r
2700     case BlackPawn:\r
2701         return PM_BP;\r
2702     case BlackKnight:\r
2703         return PM_BN;\r
2704     case BlackBishop:\r
2705         return PM_BB;\r
2706     case BlackRook:\r
2707         return PM_BR;\r
2708     case BlackQueen:\r
2709         return PM_BQ;\r
2710     case BlackKing:\r
2711         return PM_BK;\r
2712     case WhitePawn:\r
2713         return PM_WP;\r
2714     case WhiteKnight:\r
2715         return PM_WN;\r
2716     case WhiteBishop:\r
2717         return PM_WB;\r
2718     case WhiteRook:\r
2719         return PM_WR;\r
2720     case WhiteQueen:\r
2721         return PM_WQ;\r
2722     case WhiteKing:\r
2723         return PM_WK;\r
2724 \r
2725     case BlackAngel:\r
2726         return PM_BA;\r
2727     case BlackMarshall:\r
2728         return PM_BC;\r
2729     case BlackFerz:\r
2730         return PM_BF;\r
2731     case BlackNightrider:\r
2732         return PM_BH;\r
2733     case BlackAlfil:\r
2734         return PM_BE;\r
2735     case BlackWazir:\r
2736         return PM_BW;\r
2737     case BlackUnicorn:\r
2738         return PM_BU;\r
2739     case BlackCannon:\r
2740         return PM_BO;\r
2741     case BlackGrasshopper:\r
2742         return PM_BG;\r
2743     case BlackMan:\r
2744         return PM_BM;\r
2745     case BlackSilver:\r
2746         return PM_BSG;\r
2747     case BlackLance:\r
2748         return PM_BL;\r
2749     case BlackFalcon:\r
2750         return PM_BV;\r
2751     case BlackCobra:\r
2752         return PM_BS;\r
2753     case BlackCardinal:\r
2754         return PM_BAB;\r
2755     case BlackDragon:\r
2756         return PM_BD;\r
2757 \r
2758     case WhiteAngel:\r
2759         return PM_WA;\r
2760     case WhiteMarshall:\r
2761         return PM_WC;\r
2762     case WhiteFerz:\r
2763         return PM_WF;\r
2764     case WhiteNightrider:\r
2765         return PM_WH;\r
2766     case WhiteAlfil:\r
2767         return PM_WE;\r
2768     case WhiteWazir:\r
2769         return PM_WW;\r
2770     case WhiteUnicorn:\r
2771         return PM_WU;\r
2772     case WhiteCannon:\r
2773         return PM_WO;\r
2774     case WhiteGrasshopper:\r
2775         return PM_WG;\r
2776     case WhiteMan:\r
2777         return PM_WM;\r
2778     case WhiteSilver:\r
2779         return PM_WSG;\r
2780     case WhiteLance:\r
2781         return PM_WL;\r
2782     case WhiteFalcon:\r
2783         return PM_WV;\r
2784     case WhiteCobra:\r
2785         return PM_WS;\r
2786     case WhiteCardinal:\r
2787         return PM_WAB;\r
2788     case WhiteDragon:\r
2789         return PM_WD;\r
2790     }\r
2791 \r
2792     return 0;\r
2793 }\r
2794 \r
2795 void CreatePiecesFromFont()\r
2796 {\r
2797     LOGFONT lf;\r
2798     HDC hdc_window = NULL;\r
2799     HDC hdc = NULL;\r
2800     HFONT hfont_old;\r
2801     int fontHeight;\r
2802     int i;\r
2803 \r
2804     if( fontBitmapSquareSize < 0 ) {\r
2805         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2806         return;\r
2807     }\r
2808 \r
2809     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2810         fontBitmapSquareSize = -1;\r
2811         return;\r
2812     }\r
2813 \r
2814     if( fontBitmapSquareSize != squareSize ) {\r
2815         hdc_window = GetDC( hwndMain );\r
2816         hdc = CreateCompatibleDC( hdc_window );\r
2817 \r
2818         if( hPieceFont != NULL ) {\r
2819             DeleteObject( hPieceFont );\r
2820         }\r
2821         else {\r
2822             for( i=0; i<=(int)BlackKing; i++ ) {\r
2823                 hPieceMask[i] = NULL;\r
2824                 hPieceFace[i] = NULL;\r
2825             }\r
2826         }\r
2827 \r
2828         fontHeight = 75;\r
2829 \r
2830         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2831             fontHeight = appData.fontPieceSize;\r
2832         }\r
2833 \r
2834         fontHeight = (fontHeight * squareSize) / 100;\r
2835 \r
2836         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2837         lf.lfWidth = 0;\r
2838         lf.lfEscapement = 0;\r
2839         lf.lfOrientation = 0;\r
2840         lf.lfWeight = FW_NORMAL;\r
2841         lf.lfItalic = 0;\r
2842         lf.lfUnderline = 0;\r
2843         lf.lfStrikeOut = 0;\r
2844         lf.lfCharSet = DEFAULT_CHARSET;\r
2845         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2846         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2847         lf.lfQuality = PROOF_QUALITY;\r
2848         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2849         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2850         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2851 \r
2852         hPieceFont = CreateFontIndirect( &lf );\r
2853 \r
2854         if( hPieceFont == NULL ) {\r
2855             fontBitmapSquareSize = -2;\r
2856         }\r
2857         else {\r
2858             /* Setup font-to-piece character table */\r
2859             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2860                 /* No (or wrong) global settings, try to detect the font */\r
2861                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2862                     /* Alpha */\r
2863                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2864                 }\r
2865                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2866                     /* DiagramTT* family */\r
2867                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2868                 }\r
2869                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2870                     /* Fairy symbols */\r
2871                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2872                 }\r
2873                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2874                     /* Good Companion (Some characters get warped as literal :-( */\r
2875                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2876                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2877                     SetCharTable(pieceToFontChar, s);\r
2878                 }\r
2879                 else {\r
2880                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2881                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2882                 }\r
2883             }\r
2884 \r
2885             /* Create bitmaps */\r
2886             hfont_old = SelectObject( hdc, hPieceFont );\r
2887 #if 0\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2900 \r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2933 #else\r
2934             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2935                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2936                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2937 #endif\r
2938             SelectObject( hdc, hfont_old );\r
2939 \r
2940             fontBitmapSquareSize = squareSize;\r
2941         }\r
2942     }\r
2943 \r
2944     if( hdc != NULL ) {\r
2945         DeleteDC( hdc );\r
2946     }\r
2947 \r
2948     if( hdc_window != NULL ) {\r
2949         ReleaseDC( hwndMain, hdc_window );\r
2950     }\r
2951 }\r
2952 \r
2953 HBITMAP\r
2954 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2955 {\r
2956   char name[128];\r
2957 \r
2958   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2959   if (gameInfo.event &&\r
2960       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2961       strcmp(name, "k80s") == 0) {\r
2962     strcpy(name, "tim");\r
2963   }\r
2964   return LoadBitmap(hinst, name);\r
2965 }\r
2966 \r
2967 \r
2968 /* Insert a color into the program's logical palette\r
2969    structure.  This code assumes the given color is\r
2970    the result of the RGB or PALETTERGB macro, and it\r
2971    knows how those macros work (which is documented).\r
2972 */\r
2973 VOID\r
2974 InsertInPalette(COLORREF color)\r
2975 {\r
2976   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2977 \r
2978   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2979     DisplayFatalError("Too many colors", 0, 1);\r
2980     pLogPal->palNumEntries--;\r
2981     return;\r
2982   }\r
2983 \r
2984   pe->peFlags = (char) 0;\r
2985   pe->peRed = (char) (0xFF & color);\r
2986   pe->peGreen = (char) (0xFF & (color >> 8));\r
2987   pe->peBlue = (char) (0xFF & (color >> 16));\r
2988   return;\r
2989 }\r
2990 \r
2991 \r
2992 VOID\r
2993 InitDrawingColors()\r
2994 {\r
2995   if (pLogPal == NULL) {\r
2996     /* Allocate enough memory for a logical palette with\r
2997      * PALETTESIZE entries and set the size and version fields\r
2998      * of the logical palette structure.\r
2999      */\r
3000     pLogPal = (NPLOGPALETTE)\r
3001       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3002                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3003     pLogPal->palVersion    = 0x300;\r
3004   }\r
3005   pLogPal->palNumEntries = 0;\r
3006 \r
3007   InsertInPalette(lightSquareColor);\r
3008   InsertInPalette(darkSquareColor);\r
3009   InsertInPalette(whitePieceColor);\r
3010   InsertInPalette(blackPieceColor);\r
3011   InsertInPalette(highlightSquareColor);\r
3012   InsertInPalette(premoveHighlightColor);\r
3013 \r
3014   /*  create a logical color palette according the information\r
3015    *  in the LOGPALETTE structure.\r
3016    */\r
3017   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3018 \r
3019   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3020   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3021   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3022   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3023   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3024   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3025   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3026   /* [AS] Force rendering of the font-based pieces */\r
3027   if( fontBitmapSquareSize > 0 ) {\r
3028     fontBitmapSquareSize = 0;\r
3029   }\r
3030 }\r
3031 \r
3032 \r
3033 int\r
3034 BoardWidth(int boardSize, int n)\r
3035 { /* [HGM] argument n added to allow different width and height */\r
3036   int lineGap = sizeInfo[boardSize].lineGap;\r
3037 \r
3038   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3039       lineGap = appData.overrideLineGap;\r
3040   }\r
3041 \r
3042   return (n + 1) * lineGap +\r
3043           n * sizeInfo[boardSize].squareSize;\r
3044 }\r
3045 \r
3046 /* Respond to board resize by dragging edge */\r
3047 VOID\r
3048 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3049 {\r
3050   BoardSize newSize = NUM_SIZES - 1;\r
3051   static int recurse = 0;\r
3052   if (IsIconic(hwndMain)) return;\r
3053   if (recurse > 0) return;\r
3054   recurse++;\r
3055   while (newSize > 0) {\r
3056         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3057         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3058            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3059     newSize--;\r
3060   } \r
3061   boardSize = newSize;\r
3062   InitDrawingSizes(boardSize, flags);\r
3063   recurse--;\r
3064 }\r
3065 \r
3066 \r
3067 \r
3068 VOID\r
3069 InitDrawingSizes(BoardSize boardSize, int flags)\r
3070 {\r
3071   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3072   ChessSquare piece;\r
3073   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3074   HDC hdc;\r
3075   SIZE clockSize, messageSize;\r
3076   HFONT oldFont;\r
3077   char buf[MSG_SIZ];\r
3078   char *str;\r
3079   HMENU hmenu = GetMenu(hwndMain);\r
3080   RECT crect, wrect;\r
3081   int offby;\r
3082   LOGBRUSH logbrush;\r
3083 \r
3084   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3085   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3086 \r
3087   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3088   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3089 \r
3090   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3091   smallLayout = sizeInfo[boardSize].smallLayout;\r
3092   squareSize = sizeInfo[boardSize].squareSize;\r
3093   lineGap = sizeInfo[boardSize].lineGap;\r
3094   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3095 \r
3096   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3097       lineGap = appData.overrideLineGap;\r
3098   }\r
3099 \r
3100   if (tinyLayout != oldTinyLayout) {\r
3101     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3102     if (tinyLayout) {\r
3103       style &= ~WS_SYSMENU;\r
3104       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3105                  "&Minimize\tCtrl+F4");\r
3106     } else {\r
3107       style |= WS_SYSMENU;\r
3108       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3109     }\r
3110     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3111 \r
3112     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3113       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3114         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3115     }\r
3116     DrawMenuBar(hwndMain);\r
3117   }\r
3118 \r
3119   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3120   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3121 \r
3122   /* Get text area sizes */\r
3123   hdc = GetDC(hwndMain);\r
3124   if (appData.clockMode) {\r
3125     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3126   } else {\r
3127     sprintf(buf, "White");\r
3128   }\r
3129   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3130   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3131   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3132   str = "We only care about the height here";\r
3133   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3134   SelectObject(hdc, oldFont);\r
3135   ReleaseDC(hwndMain, hdc);\r
3136 \r
3137   /* Compute where everything goes */\r
3138   if(first.programLogo || second.programLogo) {\r
3139         /* [HGM] logo: if either logo is on, reserve space for it */\r
3140         logoHeight =  2*clockSize.cy;\r
3141         leftLogoRect.left   = OUTER_MARGIN;\r
3142         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3143         leftLogoRect.top    = OUTER_MARGIN;\r
3144         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3145 \r
3146         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3147         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3148         rightLogoRect.top    = OUTER_MARGIN;\r
3149         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3150 \r
3151 \r
3152     blackRect.left = leftLogoRect.right;\r
3153     blackRect.right = rightLogoRect.left;\r
3154     blackRect.top = OUTER_MARGIN;\r
3155     blackRect.bottom = blackRect.top + clockSize.cy;\r
3156 \r
3157     whiteRect.left = blackRect.left ;\r
3158     whiteRect.right = blackRect.right;\r
3159     whiteRect.top = blackRect.bottom;\r
3160     whiteRect.bottom = leftLogoRect.bottom;\r
3161   } else {\r
3162     whiteRect.left = OUTER_MARGIN;\r
3163     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3164     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3165     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3166 \r
3167     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3168     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3169     blackRect.top = whiteRect.top;\r
3170     blackRect.bottom = whiteRect.bottom;\r
3171   }\r
3172 \r
3173   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3174   if (appData.showButtonBar) {\r
3175     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3176       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3177   } else {\r
3178     messageRect.right = OUTER_MARGIN + boardWidth;\r
3179   }\r
3180   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3181   messageRect.bottom = messageRect.top + messageSize.cy;\r
3182 \r
3183   boardRect.left = OUTER_MARGIN;\r
3184   boardRect.right = boardRect.left + boardWidth;\r
3185   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3186   boardRect.bottom = boardRect.top + boardHeight;\r
3187 \r
3188   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3189   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3190   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3191   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3192   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3193     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3194   GetWindowRect(hwndMain, &wrect);\r
3195   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3196                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3197   /* compensate if menu bar wrapped */\r
3198   GetClientRect(hwndMain, &crect);\r
3199   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3200   winHeight += offby;\r
3201   switch (flags) {\r
3202   case WMSZ_TOPLEFT:\r
3203     SetWindowPos(hwndMain, NULL, \r
3204                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3205                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3206     break;\r
3207 \r
3208   case WMSZ_TOPRIGHT:\r
3209   case WMSZ_TOP:\r
3210     SetWindowPos(hwndMain, NULL, \r
3211                  wrect.left, wrect.bottom - winHeight, \r
3212                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3213     break;\r
3214 \r
3215   case WMSZ_BOTTOMLEFT:\r
3216   case WMSZ_LEFT:\r
3217     SetWindowPos(hwndMain, NULL, \r
3218                  wrect.right - winWidth, wrect.top, \r
3219                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3220     break;\r
3221 \r
3222   case WMSZ_BOTTOMRIGHT:\r
3223   case WMSZ_BOTTOM:\r
3224   case WMSZ_RIGHT:\r
3225   default:\r
3226     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3227                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3228     break;\r
3229   }\r
3230 \r
3231   hwndPause = NULL;\r
3232   for (i = 0; i < N_BUTTONS; i++) {\r
3233     if (buttonDesc[i].hwnd != NULL) {\r
3234       DestroyWindow(buttonDesc[i].hwnd);\r
3235       buttonDesc[i].hwnd = NULL;\r
3236     }\r
3237     if (appData.showButtonBar) {\r
3238       buttonDesc[i].hwnd =\r
3239         CreateWindow("BUTTON", buttonDesc[i].label,\r
3240                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3241                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3242                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3243                      (HMENU) buttonDesc[i].id,\r
3244                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3245       if (tinyLayout) {\r
3246         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3247                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3248                     MAKELPARAM(FALSE, 0));\r
3249       }\r
3250       if (buttonDesc[i].id == IDM_Pause)\r
3251         hwndPause = buttonDesc[i].hwnd;\r
3252       buttonDesc[i].wndproc = (WNDPROC)\r
3253         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3254     }\r
3255   }\r
3256   if (gridPen != NULL) DeleteObject(gridPen);\r
3257   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3258   if (premovePen != NULL) DeleteObject(premovePen);\r
3259   if (lineGap != 0) {\r
3260     logbrush.lbStyle = BS_SOLID;\r
3261     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3262     gridPen =\r
3263       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3264                    lineGap, &logbrush, 0, NULL);\r
3265     logbrush.lbColor = highlightSquareColor;\r
3266     highlightPen =\r
3267       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3268                    lineGap, &logbrush, 0, NULL);\r
3269 \r
3270     logbrush.lbColor = premoveHighlightColor; \r
3271     premovePen =\r
3272       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3273                    lineGap, &logbrush, 0, NULL);\r
3274 \r
3275     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3276     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3277       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3278       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3279         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3280       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3281         BOARD_WIDTH * (squareSize + lineGap);\r
3282       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3283     }\r
3284     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3285       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3286       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3287         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3288         lineGap / 2 + (i * (squareSize + lineGap));\r
3289       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3290         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3291       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3292     }\r
3293   }\r
3294 \r
3295   /* [HGM] Licensing requirement */\r
3296 #ifdef GOTHIC\r
3297   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3298 #endif\r
3299 #ifdef FALCON\r
3300   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3301 #endif\r
3302   GothicPopUp( "", VariantNormal);\r
3303 \r
3304 \r
3305 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3306   oldBoardSize = boardSize;\r
3307   oldTinyLayout = tinyLayout;\r
3308 \r
3309   /* Load piece bitmaps for this board size */\r
3310   for (i=0; i<=2; i++) {\r
3311     for (piece = WhitePawn;\r
3312          (int) piece < (int) BlackPawn;\r
3313          piece = (ChessSquare) ((int) piece + 1)) {\r
3314       if (pieceBitmap[i][piece] != NULL)\r
3315         DeleteObject(pieceBitmap[i][piece]);\r
3316     }\r
3317   }\r
3318 \r
3319   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3320   // Orthodox Chess pieces\r
3321   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3322   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3323   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3324   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3325   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3326   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3327   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3328   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3329   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3330   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3331   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3332   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3333   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3334   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3335   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3336   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3337     // in Shogi, Hijack the unused Queen for Lance\r
3338     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3339     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3340     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3341   } else {\r
3342     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3343     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3344     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3345   }\r
3346 \r
3347   if(squareSize <= 72 && squareSize >= 33) { \r
3348     /* A & C are available in most sizes now */\r
3349     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3350       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3351       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3352       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3353       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3354       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3355       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3356       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3357       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3358       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3359       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3360       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3361       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3362     } else { // Smirf-like\r
3363       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3364       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3365       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3366     }\r
3367     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3368       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3369       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3370       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3371     } else { // WinBoard standard\r
3372       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3373       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3374       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3375     }\r
3376   }\r
3377 \r
3378 \r
3379   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3380     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3381     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3382     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3383     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3384     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3385     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3386     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3387     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3388     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3389     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3390     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3391     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3392     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3393     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3394     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3395     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3396     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3397     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3398     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3399     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3400     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3401     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3402     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3403     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3404     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3405     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3406     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3407     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3408     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3409     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3410 \r
3411     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3412       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3413       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3414       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3415       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3416       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3417       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3418       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3419       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3420       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3421       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3422       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3423       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3424     } else {\r
3425       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3426       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3427       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3428       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3429       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3430       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3431       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3432       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3433       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3434       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3435       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3436       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3437     }\r
3438 \r
3439   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3440     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3441     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3442     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3443     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3444     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3445     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3446     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3447     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3448     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3449     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3450     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3451     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3452     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3453     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3454   }\r
3455 \r
3456 \r
3457   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3458   /* special Shogi support in this size */\r
3459   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3460       for (piece = WhitePawn;\r
3461            (int) piece < (int) BlackPawn;\r
3462            piece = (ChessSquare) ((int) piece + 1)) {\r
3463         if (pieceBitmap[i][piece] != NULL)\r
3464           DeleteObject(pieceBitmap[i][piece]);\r
3465       }\r
3466     }\r
3467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3471   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3472   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3473   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3474   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3475   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3476   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3477   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3478   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3479   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3480   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3481   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3482   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3483   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3484   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3485   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3486   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3487   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3488   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3489   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3490   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3491   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3492   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3493   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3494   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3495   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3496   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3497   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3498   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3499   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3500   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3501   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3502   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3503   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3504   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3505   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3506   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3507   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3508   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3509   minorSize = 0;\r
3510   }\r
3511 }\r
3512 \r
3513 HBITMAP\r
3514 PieceBitmap(ChessSquare p, int kind)\r
3515 {\r
3516   if ((int) p >= (int) BlackPawn)\r
3517     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3518 \r
3519   return pieceBitmap[kind][(int) p];\r
3520 }\r
3521 \r
3522 /***************************************************************/\r
3523 \r
3524 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3525 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3526 /*\r
3527 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3528 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3529 */\r
3530 \r
3531 VOID\r
3532 SquareToPos(int row, int column, int * x, int * y)\r
3533 {\r
3534   if (flipView) {\r
3535     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3536     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3537   } else {\r
3538     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3539     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3540   }\r
3541 }\r
3542 \r
3543 VOID\r
3544 DrawCoordsOnDC(HDC hdc)\r
3545 {\r
3546   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
3547   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
3548   char str[2] = { NULLCHAR, NULLCHAR };\r
3549   int oldMode, oldAlign, x, y, start, i;\r
3550   HFONT oldFont;\r
3551   HBRUSH oldBrush;\r
3552 \r
3553   if (!appData.showCoords)\r
3554     return;\r
3555 \r
3556   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3557 \r
3558   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3559   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3560   oldAlign = GetTextAlign(hdc);\r
3561   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3562 \r
3563   y = boardRect.top + lineGap;\r
3564   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3565 \r
3566   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3567   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3568     str[0] = files[start + i];\r
3569     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3570     y += squareSize + lineGap;\r
3571   }\r
3572 \r
3573   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3574 \r
3575   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3576   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3577     str[0] = ranks[start + i];\r
3578     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3579     x += squareSize + lineGap;\r
3580   }    \r
3581 \r
3582   SelectObject(hdc, oldBrush);\r
3583   SetBkMode(hdc, oldMode);\r
3584   SetTextAlign(hdc, oldAlign);\r
3585   SelectObject(hdc, oldFont);\r
3586 }\r
3587 \r
3588 VOID\r
3589 DrawGridOnDC(HDC hdc)\r
3590 {\r
3591   HPEN oldPen;\r
3592  \r
3593   if (lineGap != 0) {\r
3594     oldPen = SelectObject(hdc, gridPen);\r
3595     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3596     SelectObject(hdc, oldPen);\r
3597   }\r
3598 }\r
3599 \r
3600 #define HIGHLIGHT_PEN 0\r
3601 #define PREMOVE_PEN   1\r
3602 \r
3603 VOID\r
3604 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3605 {\r
3606   int x1, y1;\r
3607   HPEN oldPen, hPen;\r
3608   if (lineGap == 0) return;\r
3609   if (flipView) {\r
3610     x1 = boardRect.left +\r
3611       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3612     y1 = boardRect.top +\r
3613       lineGap/2 + y * (squareSize + lineGap);\r
3614   } else {\r
3615     x1 = boardRect.left +\r
3616       lineGap/2 + x * (squareSize + lineGap);\r
3617     y1 = boardRect.top +\r
3618       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3619   }\r
3620   hPen = pen ? premovePen : highlightPen;\r
3621   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3622   MoveToEx(hdc, x1, y1, NULL);\r
3623   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3624   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3625   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3626   LineTo(hdc, x1, y1);\r
3627   SelectObject(hdc, oldPen);\r
3628 }\r
3629 \r
3630 VOID\r
3631 DrawHighlightsOnDC(HDC hdc)\r
3632 {\r
3633   int i;\r
3634   for (i=0; i<2; i++) {\r
3635     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3636       DrawHighlightOnDC(hdc, TRUE,\r
3637                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3638                         HIGHLIGHT_PEN);\r
3639   }\r
3640   for (i=0; i<2; i++) {\r
3641     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3642         premoveHighlightInfo.sq[i].y >= 0) {\r
3643         DrawHighlightOnDC(hdc, TRUE,\r
3644                           premoveHighlightInfo.sq[i].x, \r
3645                           premoveHighlightInfo.sq[i].y,\r
3646                           PREMOVE_PEN);\r
3647     }\r
3648   }\r
3649 }\r
3650 \r
3651 /* Note: sqcolor is used only in monoMode */\r
3652 /* Note that this code is largely duplicated in woptions.c,\r
3653    function DrawSampleSquare, so that needs to be updated too */\r
3654 VOID\r
3655 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3656 {\r
3657   HBITMAP oldBitmap;\r
3658   HBRUSH oldBrush;\r
3659   int tmpSize;\r
3660 \r
3661   if (appData.blindfold) return;\r
3662 \r
3663   /* [AS] Use font-based pieces if needed */\r
3664   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3665     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3666     CreatePiecesFromFont();\r
3667 \r
3668     if( fontBitmapSquareSize == squareSize ) {\r
3669         int index = TranslatePieceToFontPiece(piece);\r
3670 \r
3671         SelectObject( tmphdc, hPieceMask[ index ] );\r
3672 \r
3673         BitBlt( hdc,\r
3674             x, y,\r
3675             squareSize, squareSize,\r
3676             tmphdc,\r
3677             0, 0,\r
3678             SRCAND );\r
3679 \r
3680         SelectObject( tmphdc, hPieceFace[ index ] );\r
3681 \r
3682         BitBlt( hdc,\r
3683             x, y,\r
3684             squareSize, squareSize,\r
3685             tmphdc,\r
3686             0, 0,\r
3687             SRCPAINT );\r
3688 \r
3689         return;\r
3690     }\r
3691   }\r
3692 \r
3693   if (appData.monoMode) {\r
3694     SelectObject(tmphdc, PieceBitmap(piece, \r
3695       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3696     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3697            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3698   } else {\r
3699     tmpSize = squareSize;\r
3700     if(minorSize &&\r
3701         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3702          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3703       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3704       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3705       x += (squareSize - minorSize)>>1;\r
3706       y += squareSize - minorSize - 2;\r
3707       tmpSize = minorSize;\r
3708     }\r
3709     if (color || appData.allWhite ) {\r
3710       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3711       if( color )\r
3712               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3713       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3714       if(appData.upsideDown && color==flipView)\r
3715         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3716       else\r
3717         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3718 #if 0\r
3719       /* Use black piece color for outline of white pieces */\r
3720       /* Not sure this looks really good (though xboard does it).\r
3721          Maybe better to have another selectable color, default black */\r
3722       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3723       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3724       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3725 #else\r
3726       /* Use black for outline of white pieces */\r
3727       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3728       if(appData.upsideDown && color==flipView)\r
3729         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3730       else\r
3731         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3732 #endif\r
3733     } else {\r
3734 #if 0\r
3735       /* Use white piece color for details of black pieces */\r
3736       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3737          WHITE_PIECE ones aren't always the right shape. */\r
3738       /* Not sure this looks really good (though xboard does it).\r
3739          Maybe better to have another selectable color, default medium gray? */\r
3740       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3741       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3742       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3743       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3744       SelectObject(hdc, blackPieceBrush);\r
3745       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3746 #else\r
3747       /* Use square color for details of black pieces */\r
3748       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3749       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3750       if(appData.upsideDown && !flipView)\r
3751         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3752       else\r
3753         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3754 #endif\r
3755     }\r
3756     SelectObject(hdc, oldBrush);\r
3757     SelectObject(tmphdc, oldBitmap);\r
3758   }\r
3759 }\r
3760 \r
3761 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3762 int GetBackTextureMode( int algo )\r
3763 {\r
3764     int result = BACK_TEXTURE_MODE_DISABLED;\r
3765 \r
3766     switch( algo ) \r
3767     {\r
3768         case BACK_TEXTURE_MODE_PLAIN:\r
3769             result = 1; /* Always use identity map */\r
3770             break;\r
3771         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3772             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3773             break;\r
3774     }\r
3775 \r
3776     return result;\r
3777 }\r
3778 \r
3779 /* \r
3780     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3781     to handle redraws cleanly (as random numbers would always be different).\r
3782 */\r
3783 VOID RebuildTextureSquareInfo()\r
3784 {\r
3785     BITMAP bi;\r
3786     int lite_w = 0;\r
3787     int lite_h = 0;\r
3788     int dark_w = 0;\r
3789     int dark_h = 0;\r
3790     int row;\r
3791     int col;\r
3792 \r
3793     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3794 \r
3795     if( liteBackTexture != NULL ) {\r
3796         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3797             lite_w = bi.bmWidth;\r
3798             lite_h = bi.bmHeight;\r
3799         }\r
3800     }\r
3801 \r
3802     if( darkBackTexture != NULL ) {\r
3803         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3804             dark_w = bi.bmWidth;\r
3805             dark_h = bi.bmHeight;\r
3806         }\r
3807     }\r
3808 \r
3809     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3810         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3811             if( (col + row) & 1 ) {\r
3812                 /* Lite square */\r
3813                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3814                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3815                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3816                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3817                 }\r
3818             }\r
3819             else {\r
3820                 /* Dark square */\r
3821                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3822                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3823                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3824                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3825                 }\r
3826             }\r
3827         }\r
3828     }\r
3829 }\r
3830 \r
3831 /* [AS] Arrow highlighting support */\r
3832 \r
3833 static int A_WIDTH = 5; /* Width of arrow body */\r
3834 \r
3835 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3836 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3837 \r
3838 static double Sqr( double x )\r
3839 {\r
3840     return x*x;\r
3841 }\r
3842 \r
3843 static int Round( double x )\r
3844 {\r
3845     return (int) (x + 0.5);\r
3846 }\r
3847 \r
3848 /* Draw an arrow between two points using current settings */\r
3849 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3850 {\r
3851     POINT arrow[7];\r
3852     double dx, dy, j, k, x, y;\r
3853 \r
3854     if( d_x == s_x ) {\r
3855         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3856 \r
3857         arrow[0].x = s_x + A_WIDTH;\r
3858         arrow[0].y = s_y;\r
3859 \r
3860         arrow[1].x = s_x + A_WIDTH;\r
3861         arrow[1].y = d_y - h;\r
3862 \r
3863         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3864         arrow[2].y = d_y - h;\r
3865 \r
3866         arrow[3].x = d_x;\r
3867         arrow[3].y = d_y;\r
3868 \r
3869         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3870         arrow[4].y = d_y - h;\r
3871 \r
3872         arrow[5].x = s_x - A_WIDTH;\r
3873         arrow[5].y = d_y - h;\r
3874 \r
3875         arrow[6].x = s_x - A_WIDTH;\r
3876         arrow[6].y = s_y;\r
3877     }\r
3878     else if( d_y == s_y ) {\r
3879         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3880 \r
3881         arrow[0].x = s_x;\r
3882         arrow[0].y = s_y + A_WIDTH;\r
3883 \r
3884         arrow[1].x = d_x - w;\r
3885         arrow[1].y = s_y + A_WIDTH;\r
3886 \r
3887         arrow[2].x = d_x - w;\r
3888         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3889 \r
3890         arrow[3].x = d_x;\r
3891         arrow[3].y = d_y;\r
3892 \r
3893         arrow[4].x = d_x - w;\r
3894         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3895 \r
3896         arrow[5].x = d_x - w;\r
3897         arrow[5].y = s_y - A_WIDTH;\r
3898 \r
3899         arrow[6].x = s_x;\r
3900         arrow[6].y = s_y - A_WIDTH;\r
3901     }\r
3902     else {\r
3903         /* [AS] Needed a lot of paper for this! :-) */\r
3904         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3905         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3906   \r
3907         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3908 \r
3909         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3910 \r
3911         x = s_x;\r
3912         y = s_y;\r
3913 \r
3914         arrow[0].x = Round(x - j);\r
3915         arrow[0].y = Round(y + j*dx);\r
3916 \r
3917         arrow[1].x = Round(x + j);\r
3918         arrow[1].y = Round(y - j*dx);\r
3919 \r
3920         if( d_x > s_x ) {\r
3921             x = (double) d_x - k;\r
3922             y = (double) d_y - k*dy;\r
3923         }\r
3924         else {\r
3925             x = (double) d_x + k;\r
3926             y = (double) d_y + k*dy;\r
3927         }\r
3928 \r
3929         arrow[2].x = Round(x + j);\r
3930         arrow[2].y = Round(y - j*dx);\r
3931 \r
3932         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3933         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3934 \r
3935         arrow[4].x = d_x;\r
3936         arrow[4].y = d_y;\r
3937 \r
3938         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3939         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3940 \r
3941         arrow[6].x = Round(x - j);\r
3942         arrow[6].y = Round(y + j*dx);\r
3943     }\r
3944 \r
3945     Polygon( hdc, arrow, 7 );\r
3946 }\r
3947 \r
3948 /* [AS] Draw an arrow between two squares */\r
3949 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3950 {\r
3951     int s_x, s_y, d_x, d_y;\r
3952     HPEN hpen;\r
3953     HPEN holdpen;\r
3954     HBRUSH hbrush;\r
3955     HBRUSH holdbrush;\r
3956     LOGBRUSH stLB;\r
3957 \r
3958     if( s_col == d_col && s_row == d_row ) {\r
3959         return;\r
3960     }\r
3961 \r
3962     /* Get source and destination points */\r
3963     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3964     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3965 \r
3966     if( d_y > s_y ) {\r
3967         d_y += squareSize / 4;\r
3968     }\r
3969     else if( d_y < s_y ) {\r
3970         d_y += 3 * squareSize / 4;\r
3971     }\r
3972     else {\r
3973         d_y += squareSize / 2;\r
3974     }\r
3975 \r
3976     if( d_x > s_x ) {\r
3977         d_x += squareSize / 4;\r
3978     }\r
3979     else if( d_x < s_x ) {\r
3980         d_x += 3 * squareSize / 4;\r
3981     }\r
3982     else {\r
3983         d_x += squareSize / 2;\r
3984     }\r
3985 \r
3986     s_x += squareSize / 2;\r
3987     s_y += squareSize / 2;\r
3988 \r
3989     /* Adjust width */\r
3990     A_WIDTH = squareSize / 14;\r
3991 \r
3992     /* Draw */\r
3993     stLB.lbStyle = BS_SOLID;\r
3994     stLB.lbColor = appData.highlightArrowColor;\r
3995     stLB.lbHatch = 0;\r
3996 \r
3997     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3998     holdpen = SelectObject( hdc, hpen );\r
3999     hbrush = CreateBrushIndirect( &stLB );\r
4000     holdbrush = SelectObject( hdc, hbrush );\r
4001 \r
4002     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4003 \r
4004     SelectObject( hdc, holdpen );\r
4005     SelectObject( hdc, holdbrush );\r
4006     DeleteObject( hpen );\r
4007     DeleteObject( hbrush );\r
4008 }\r
4009 \r
4010 BOOL HasHighlightInfo()\r
4011 {\r
4012     BOOL result = FALSE;\r
4013 \r
4014     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4015         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4016     {\r
4017         result = TRUE;\r
4018     }\r
4019 \r
4020     return result;\r
4021 }\r
4022 \r
4023 BOOL IsDrawArrowEnabled()\r
4024 {\r
4025     BOOL result = FALSE;\r
4026 \r
4027     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4028         result = TRUE;\r
4029     }\r
4030 \r
4031     return result;\r
4032 }\r
4033 \r
4034 VOID DrawArrowHighlight( HDC hdc )\r
4035 {\r
4036     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4037         DrawArrowBetweenSquares( hdc,\r
4038             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4039             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4040     }\r
4041 }\r
4042 \r
4043 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4044 {\r
4045     HRGN result = NULL;\r
4046 \r
4047     if( HasHighlightInfo() ) {\r
4048         int x1, y1, x2, y2;\r
4049         int sx, sy, dx, dy;\r
4050 \r
4051         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4052         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4053 \r
4054         sx = MIN( x1, x2 );\r
4055         sy = MIN( y1, y2 );\r
4056         dx = MAX( x1, x2 ) + squareSize;\r
4057         dy = MAX( y1, y2 ) + squareSize;\r
4058 \r
4059         result = CreateRectRgn( sx, sy, dx, dy );\r
4060     }\r
4061 \r
4062     return result;\r
4063 }\r
4064 \r
4065 /*\r
4066     Warning: this function modifies the behavior of several other functions. \r
4067     \r
4068     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4069     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4070     repaint is scattered all over the place, which is not good for features such as\r
4071     "arrow highlighting" that require a full repaint of the board.\r
4072 \r
4073     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4074     user interaction, when speed is not so important) but especially to avoid errors\r
4075     in the displayed graphics.\r
4076 \r
4077     In such patched places, I always try refer to this function so there is a single\r
4078     place to maintain knowledge.\r
4079     \r
4080     To restore the original behavior, just return FALSE unconditionally.\r
4081 */\r
4082 BOOL IsFullRepaintPreferrable()\r
4083 {\r
4084     BOOL result = FALSE;\r
4085 \r
4086     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4087         /* Arrow may appear on the board */\r
4088         result = TRUE;\r
4089     }\r
4090 \r
4091     return result;\r
4092 }\r
4093 \r
4094 /* \r
4095     This function is called by DrawPosition to know whether a full repaint must\r
4096     be forced or not.\r
4097 \r
4098     Only DrawPosition may directly call this function, which makes use of \r
4099     some state information. Other function should call DrawPosition specifying \r
4100     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4101 */\r
4102 BOOL DrawPositionNeedsFullRepaint()\r
4103 {\r
4104     BOOL result = FALSE;\r
4105 \r
4106     /* \r
4107         Probably a slightly better policy would be to trigger a full repaint\r
4108         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4109         but animation is fast enough that it's difficult to notice.\r
4110     */\r
4111     if( animInfo.piece == EmptySquare ) {\r
4112         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4113             result = TRUE;\r
4114         }\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 VOID\r
4121 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4122 {\r
4123   int row, column, x, y, square_color, piece_color;\r
4124   ChessSquare piece;\r
4125   HBRUSH oldBrush;\r
4126   HDC texture_hdc = NULL;\r
4127 \r
4128   /* [AS] Initialize background textures if needed */\r
4129   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4130       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4131       if( backTextureSquareSize != squareSize \r
4132        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4133           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4134           backTextureSquareSize = squareSize;\r
4135           RebuildTextureSquareInfo();\r
4136       }\r
4137 \r
4138       texture_hdc = CreateCompatibleDC( hdc );\r
4139   }\r
4140 \r
4141   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4142     for (column = 0; column < BOARD_WIDTH; column++) {\r
4143   \r
4144       SquareToPos(row, column, &x, &y);\r
4145 \r
4146       piece = board[row][column];\r
4147 \r
4148       square_color = ((column + row) % 2) == 1;\r
4149       if( gameInfo.variant == VariantXiangqi ) {\r
4150           square_color = !InPalace(row, column);\r
4151           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4152           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4153       }\r
4154       piece_color = (int) piece < (int) BlackPawn;\r
4155 \r
4156 \r
4157       /* [HGM] holdings file: light square or black */\r
4158       if(column == BOARD_LEFT-2) {\r
4159             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4160                 square_color = 1;\r
4161             else {\r
4162                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4163                 continue;\r
4164             }\r
4165       } else\r
4166       if(column == BOARD_RGHT + 1 ) {\r
4167             if( row < gameInfo.holdingsSize )\r
4168                 square_color = 1;\r
4169             else {\r
4170                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4171                 continue;\r
4172             }\r
4173       }\r
4174       if(column == BOARD_LEFT-1 ) /* left align */\r
4175             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4176       else if( column == BOARD_RGHT) /* right align */\r
4177             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4178       else\r
4179       if (appData.monoMode) {\r
4180         if (piece == EmptySquare) {\r
4181           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4182                  square_color ? WHITENESS : BLACKNESS);\r
4183         } else {\r
4184           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4185         }\r
4186       } \r
4187       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4188           /* [AS] Draw the square using a texture bitmap */\r
4189           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4190           int r = row, c = column; // [HGM] do not flip board in flipView\r
4191           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4192 \r
4193           DrawTile( x, y, \r
4194               squareSize, squareSize, \r
4195               hdc, \r
4196               texture_hdc,\r
4197               backTextureSquareInfo[r][c].mode,\r
4198               backTextureSquareInfo[r][c].x,\r
4199               backTextureSquareInfo[r][c].y );\r
4200 \r
4201           SelectObject( texture_hdc, hbm );\r
4202 \r
4203           if (piece != EmptySquare) {\r
4204               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4205           }\r
4206       }\r
4207       else {\r
4208         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4209 \r
4210         oldBrush = SelectObject(hdc, brush );\r
4211         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4212         SelectObject(hdc, oldBrush);\r
4213         if (piece != EmptySquare)\r
4214           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4215       }\r
4216     }\r
4217   }\r
4218 \r
4219   if( texture_hdc != NULL ) {\r
4220     DeleteDC( texture_hdc );\r
4221   }\r
4222 }\r
4223 \r
4224 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4225 void fputDW(FILE *f, int x)\r
4226 {\r
4227         fputc(x     & 255, f);\r
4228         fputc(x>>8  & 255, f);\r
4229         fputc(x>>16 & 255, f);\r
4230         fputc(x>>24 & 255, f);\r
4231 }\r
4232 \r
4233 #define MAX_CLIPS 200   /* more than enough */\r
4234 \r
4235 VOID\r
4236 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4237 {\r
4238 //  HBITMAP bufferBitmap;\r
4239   BITMAP bi;\r
4240 //  RECT Rect;\r
4241   HDC tmphdc;\r
4242   HBITMAP hbm;\r
4243   int w = 100, h = 50;\r
4244 \r
4245   if(cps->programLogo == NULL) return;\r
4246 //  GetClientRect(hwndMain, &Rect);\r
4247 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4248 //                                      Rect.bottom-Rect.top+1);\r
4249   tmphdc = CreateCompatibleDC(hdc);\r
4250   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4251   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4252             w = bi.bmWidth;\r
4253             h = bi.bmHeight;\r
4254   }\r
4255   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4256                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4257   SelectObject(tmphdc, hbm);\r
4258   DeleteDC(tmphdc);\r
4259 }\r
4260 \r
4261 VOID\r
4262 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4263 {\r
4264   static Board lastReq, lastDrawn;\r
4265   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4266   static int lastDrawnFlipView = 0;\r
4267   static int lastReqValid = 0, lastDrawnValid = 0;\r
4268   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4269   HDC tmphdc;\r
4270   HDC hdcmem;\r
4271   HBITMAP bufferBitmap;\r
4272   HBITMAP oldBitmap;\r
4273   RECT Rect;\r
4274   HRGN clips[MAX_CLIPS];\r
4275   ChessSquare dragged_piece = EmptySquare;\r
4276 \r
4277   /* I'm undecided on this - this function figures out whether a full\r
4278    * repaint is necessary on its own, so there's no real reason to have the\r
4279    * caller tell it that.  I think this can safely be set to FALSE - but\r
4280    * if we trust the callers not to request full repaints unnessesarily, then\r
4281    * we could skip some clipping work.  In other words, only request a full\r
4282    * redraw when the majority of pieces have changed positions (ie. flip, \r
4283    * gamestart and similar)  --Hawk\r
4284    */\r
4285   Boolean fullrepaint = repaint;\r
4286 \r
4287   if( DrawPositionNeedsFullRepaint() ) {\r
4288       fullrepaint = TRUE;\r
4289   }\r
4290 \r
4291 #if 0\r
4292   if( fullrepaint ) {\r
4293       static int repaint_count = 0;\r
4294       char buf[128];\r
4295 \r
4296       repaint_count++;\r
4297       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4298       OutputDebugString( buf );\r
4299   }\r
4300 #endif\r
4301 \r
4302   if (board == NULL) {\r
4303     if (!lastReqValid) {\r
4304       return;\r
4305     }\r
4306     board = lastReq;\r
4307   } else {\r
4308     CopyBoard(lastReq, board);\r
4309     lastReqValid = 1;\r
4310   }\r
4311 \r
4312   if (doingSizing) {\r
4313     return;\r
4314   }\r
4315 \r
4316   if (IsIconic(hwndMain)) {\r
4317     return;\r
4318   }\r
4319 \r
4320   if (hdc == NULL) {\r
4321     hdc = GetDC(hwndMain);\r
4322     if (!appData.monoMode) {\r
4323       SelectPalette(hdc, hPal, FALSE);\r
4324       RealizePalette(hdc);\r
4325     }\r
4326     releaseDC = TRUE;\r
4327   } else {\r
4328     releaseDC = FALSE;\r
4329   }\r
4330 \r
4331 #if 0\r
4332   fprintf(debugFP, "*******************************\n"\r
4333                    "repaint = %s\n"\r
4334                    "dragInfo.from (%d,%d)\n"\r
4335                    "dragInfo.start (%d,%d)\n"\r
4336                    "dragInfo.pos (%d,%d)\n"\r
4337                    "dragInfo.lastpos (%d,%d)\n", \r
4338                     repaint ? "TRUE" : "FALSE",\r
4339                     dragInfo.from.x, dragInfo.from.y, \r
4340                     dragInfo.start.x, dragInfo.start.y,\r
4341                     dragInfo.pos.x, dragInfo.pos.y,\r
4342                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4343   fprintf(debugFP, "prev:  ");\r
4344   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4345     for (column = 0; column < BOARD_WIDTH; column++) {\r
4346       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4347     }\r
4348   }\r
4349   fprintf(debugFP, "\n");\r
4350   fprintf(debugFP, "board: ");\r
4351   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4352     for (column = 0; column < BOARD_WIDTH; column++) {\r
4353       fprintf(debugFP, "%d ", board[row][column]);\r
4354     }\r
4355   }\r
4356   fprintf(debugFP, "\n");\r
4357   fflush(debugFP);\r
4358 #endif\r
4359 \r
4360   /* Create some work-DCs */\r
4361   hdcmem = CreateCompatibleDC(hdc);\r
4362   tmphdc = CreateCompatibleDC(hdc);\r
4363 \r
4364   /* If dragging is in progress, we temporarely remove the piece */\r
4365   /* [HGM] or temporarily decrease count if stacked              */\r
4366   /*       !! Moved to before board compare !!                   */\r
4367   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4368     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4369     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4370             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4371         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4372     } else \r
4373     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4374             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4375         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4376     } else \r
4377         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4378   }\r
4379 \r
4380   /* Figure out which squares need updating by comparing the \r
4381    * newest board with the last drawn board and checking if\r
4382    * flipping has changed.\r
4383    */\r
4384   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4385     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4386       for (column = 0; column < BOARD_WIDTH; column++) {\r
4387         if (lastDrawn[row][column] != board[row][column]) {\r
4388           SquareToPos(row, column, &x, &y);\r
4389           clips[num_clips++] =\r
4390             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4391         }\r
4392       }\r
4393     }\r
4394     for (i=0; i<2; i++) {\r
4395       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4396           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4397         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4398             lastDrawnHighlight.sq[i].y >= 0) {\r
4399           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4400                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4401           clips[num_clips++] =\r
4402             CreateRectRgn(x - lineGap, y - lineGap, \r
4403                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4404         }\r
4405         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4406           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4407           clips[num_clips++] =\r
4408             CreateRectRgn(x - lineGap, y - lineGap, \r
4409                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4410         }\r
4411       }\r
4412     }\r
4413     for (i=0; i<2; i++) {\r
4414       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4415           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4416         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4417             lastDrawnPremove.sq[i].y >= 0) {\r
4418           SquareToPos(lastDrawnPremove.sq[i].y,\r
4419                       lastDrawnPremove.sq[i].x, &x, &y);\r
4420           clips[num_clips++] =\r
4421             CreateRectRgn(x - lineGap, y - lineGap, \r
4422                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4423         }\r
4424         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4425             premoveHighlightInfo.sq[i].y >= 0) {\r
4426           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4427                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4428           clips[num_clips++] =\r
4429             CreateRectRgn(x - lineGap, y - lineGap, \r
4430                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4431         }\r
4432       }\r
4433     }\r
4434   } else {\r
4435     fullrepaint = TRUE;\r
4436   }\r
4437 \r
4438   /* Create a buffer bitmap - this is the actual bitmap\r
4439    * being written to.  When all the work is done, we can\r
4440    * copy it to the real DC (the screen).  This avoids\r
4441    * the problems with flickering.\r
4442    */\r
4443   GetClientRect(hwndMain, &Rect);\r
4444   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4445                                         Rect.bottom-Rect.top+1);\r
4446   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4447   if (!appData.monoMode) {\r
4448     SelectPalette(hdcmem, hPal, FALSE);\r
4449   }\r
4450 \r
4451   /* Create clips for dragging */\r
4452   if (!fullrepaint) {\r
4453     if (dragInfo.from.x >= 0) {\r
4454       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4455       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4456     }\r
4457     if (dragInfo.start.x >= 0) {\r
4458       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4459       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4460     }\r
4461     if (dragInfo.pos.x >= 0) {\r
4462       x = dragInfo.pos.x - squareSize / 2;\r
4463       y = dragInfo.pos.y - squareSize / 2;\r
4464       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4465     }\r
4466     if (dragInfo.lastpos.x >= 0) {\r
4467       x = dragInfo.lastpos.x - squareSize / 2;\r
4468       y = dragInfo.lastpos.y - squareSize / 2;\r
4469       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4470     }\r
4471   }\r
4472 \r
4473   /* Are we animating a move?  \r
4474    * If so, \r
4475    *   - remove the piece from the board (temporarely)\r
4476    *   - calculate the clipping region\r
4477    */\r
4478   if (!fullrepaint) {\r
4479     if (animInfo.piece != EmptySquare) {\r
4480       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4481       x = boardRect.left + animInfo.lastpos.x;\r
4482       y = boardRect.top + animInfo.lastpos.y;\r
4483       x2 = boardRect.left + animInfo.pos.x;\r
4484       y2 = boardRect.top + animInfo.pos.y;\r
4485       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4486       /* Slight kludge.  The real problem is that after AnimateMove is\r
4487          done, the position on the screen does not match lastDrawn.\r
4488          This currently causes trouble only on e.p. captures in\r
4489          atomic, where the piece moves to an empty square and then\r
4490          explodes.  The old and new positions both had an empty square\r
4491          at the destination, but animation has drawn a piece there and\r
4492          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4493       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4494     }\r
4495   }\r
4496 \r
4497   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4498   if (num_clips == 0)\r
4499     fullrepaint = TRUE;\r
4500 \r
4501   /* Set clipping on the memory DC */\r
4502   if (!fullrepaint) {\r
4503     SelectClipRgn(hdcmem, clips[0]);\r
4504     for (x = 1; x < num_clips; x++) {\r
4505       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4506         abort();  // this should never ever happen!\r
4507     }\r
4508   }\r
4509 \r
4510   /* Do all the drawing to the memory DC */\r
4511   if(explodeInfo.radius) { // [HGM] atomic\r
4512         HBRUSH oldBrush;\r
4513         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4514         SquareToPos(explodeInfo.y, explodeInfo.x, &x, &y);\r
4515         x += squareSize/2;\r
4516         y += squareSize/2;\r
4517         if(!fullrepaint) {\r
4518           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4519           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4520         }\r
4521         DrawGridOnDC(hdcmem);\r
4522         DrawHighlightsOnDC(hdcmem);\r
4523         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4524         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4525         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4526         SelectObject(hdcmem, oldBrush);\r
4527   } else {\r
4528     DrawGridOnDC(hdcmem);\r
4529     DrawHighlightsOnDC(hdcmem);\r
4530     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4531   }\r
4532   if(logoHeight) {\r
4533         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4534         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4535   }\r
4536 \r
4537   if( appData.highlightMoveWithArrow ) {\r
4538     DrawArrowHighlight(hdcmem);\r
4539   }\r
4540 \r
4541   DrawCoordsOnDC(hdcmem);\r
4542 \r
4543   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4544                  /* to make sure lastDrawn contains what is actually drawn */\r
4545 \r
4546   /* Put the dragged piece back into place and draw it (out of place!) */\r
4547     if (dragged_piece != EmptySquare) {\r
4548     /* [HGM] or restack */\r
4549     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4550                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4551     else\r
4552     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4553                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4554     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4555     x = dragInfo.pos.x - squareSize / 2;\r
4556     y = dragInfo.pos.y - squareSize / 2;\r
4557     DrawPieceOnDC(hdcmem, dragged_piece,\r
4558                   ((int) dragged_piece < (int) BlackPawn), \r
4559                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4560   }   \r
4561   \r
4562   /* Put the animated piece back into place and draw it */\r
4563   if (animInfo.piece != EmptySquare) {\r
4564     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4565     x = boardRect.left + animInfo.pos.x;\r
4566     y = boardRect.top + animInfo.pos.y;\r
4567     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4568                   ((int) animInfo.piece < (int) BlackPawn),\r
4569                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4570   }\r
4571 \r
4572   /* Release the bufferBitmap by selecting in the old bitmap \r
4573    * and delete the memory DC\r
4574    */\r
4575   SelectObject(hdcmem, oldBitmap);\r
4576   DeleteDC(hdcmem);\r
4577 \r
4578   /* Set clipping on the target DC */\r
4579   if (!fullrepaint) {\r
4580     SelectClipRgn(hdc, clips[0]);\r
4581     for (x = 1; x < num_clips; x++) {\r
4582       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4583         abort();   // this should never ever happen!\r
4584     } \r
4585   }\r
4586 \r
4587   /* Copy the new bitmap onto the screen in one go.\r
4588    * This way we avoid any flickering\r
4589    */\r
4590   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4591   BitBlt(hdc, boardRect.left, boardRect.top,\r
4592          boardRect.right - boardRect.left,\r
4593          boardRect.bottom - boardRect.top,\r
4594          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4595   if(saveDiagFlag) { \r
4596     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4597     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4598 \r
4599     GetObject(bufferBitmap, sizeof(b), &b);\r
4600     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4601         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4602         bih.biWidth = b.bmWidth;\r
4603         bih.biHeight = b.bmHeight;\r
4604         bih.biPlanes = 1;\r
4605         bih.biBitCount = b.bmBitsPixel;\r
4606         bih.biCompression = 0;\r
4607         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4608         bih.biXPelsPerMeter = 0;\r
4609         bih.biYPelsPerMeter = 0;\r
4610         bih.biClrUsed = 0;\r
4611         bih.biClrImportant = 0;\r
4612 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4613 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4614         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4615 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4616 \r
4617 #if 1\r
4618         wb = b.bmWidthBytes;\r
4619         // count colors\r
4620         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4621                 int k = ((int*) pData)[i];\r
4622                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4623                 if(j >= 16) break;\r
4624                 color[j] = k;\r
4625                 if(j >= nrColors) nrColors = j+1;\r
4626         }\r
4627         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4628                 INT p = 0;\r
4629                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4630                     for(w=0; w<(wb>>2); w+=2) {\r
4631                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4632                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4633                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4634                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4635                         pData[p++] = m | j<<4;\r
4636                     }\r
4637                     while(p&3) pData[p++] = 0;\r
4638                 }\r
4639                 fac = 3;\r
4640                 wb = ((wb+31)>>5)<<2;\r
4641         }\r
4642         // write BITMAPFILEHEADER\r
4643         fprintf(diagFile, "BM");\r
4644         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4645         fputDW(diagFile, 0);\r
4646         fputDW(diagFile, 0x36 + (fac?64:0));\r
4647         // write BITMAPINFOHEADER\r
4648         fputDW(diagFile, 40);\r
4649         fputDW(diagFile, b.bmWidth);\r
4650         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4651         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4652         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4653         fputDW(diagFile, 0);\r
4654         fputDW(diagFile, 0);\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         fputDW(diagFile, 0);\r
4658         fputDW(diagFile, 0);\r
4659         // write color table\r
4660         if(fac)\r
4661         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4662         // write bitmap data\r
4663         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4664                 fputc(pData[i], diagFile);\r
4665 #endif\r
4666      }\r
4667   }\r
4668 \r
4669   SelectObject(tmphdc, oldBitmap);\r
4670 \r
4671   /* Massive cleanup */\r
4672   for (x = 0; x < num_clips; x++)\r
4673     DeleteObject(clips[x]);\r
4674 \r
4675   DeleteDC(tmphdc);\r
4676   DeleteObject(bufferBitmap);\r
4677 \r
4678   if (releaseDC) \r
4679     ReleaseDC(hwndMain, hdc);\r
4680   \r
4681   if (lastDrawnFlipView != flipView) {\r
4682     if (flipView)\r
4683       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4684     else\r
4685       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4686   }\r
4687 \r
4688 /*  CopyBoard(lastDrawn, board);*/\r
4689   lastDrawnHighlight = highlightInfo;\r
4690   lastDrawnPremove   = premoveHighlightInfo;\r
4691   lastDrawnFlipView = flipView;\r
4692   lastDrawnValid = 1;\r
4693 }\r
4694 \r
4695 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4696 int\r
4697 SaveDiagram(f)\r
4698      FILE *f;\r
4699 {\r
4700     saveDiagFlag = 1; diagFile = f;\r
4701     HDCDrawPosition(NULL, TRUE, NULL);\r
4702 \r
4703     saveDiagFlag = 0;\r
4704 \r
4705 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4706     \r
4707     fclose(f);\r
4708     return TRUE;\r
4709 }\r
4710 \r
4711 \r
4712 /*---------------------------------------------------------------------------*\\r
4713 | CLIENT PAINT PROCEDURE\r
4714 |   This is the main event-handler for the WM_PAINT message.\r
4715 |\r
4716 \*---------------------------------------------------------------------------*/\r
4717 VOID\r
4718 PaintProc(HWND hwnd)\r
4719 {\r
4720   HDC         hdc;\r
4721   PAINTSTRUCT ps;\r
4722   HFONT       oldFont;\r
4723 \r
4724   if((hdc = BeginPaint(hwnd, &ps))) {\r
4725     if (IsIconic(hwnd)) {\r
4726       DrawIcon(hdc, 2, 2, iconCurrent);\r
4727     } else {\r
4728       if (!appData.monoMode) {\r
4729         SelectPalette(hdc, hPal, FALSE);\r
4730         RealizePalette(hdc);\r
4731       }\r
4732       HDCDrawPosition(hdc, 1, NULL);\r
4733       oldFont =\r
4734         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4735       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4736                  ETO_CLIPPED|ETO_OPAQUE,\r
4737                  &messageRect, messageText, strlen(messageText), NULL);\r
4738       SelectObject(hdc, oldFont);\r
4739       DisplayBothClocks();\r
4740     }\r
4741     EndPaint(hwnd,&ps);\r
4742   }\r
4743 \r
4744   return;\r
4745 }\r
4746 \r
4747 \r
4748 /*\r
4749  * If the user selects on a border boundary, return -1; if off the board,\r
4750  *   return -2.  Otherwise map the event coordinate to the square.\r
4751  * The offset boardRect.left or boardRect.top must already have been\r
4752  *   subtracted from x.\r
4753  */\r
4754 int\r
4755 EventToSquare(int x)\r
4756 {\r
4757   if (x <= 0)\r
4758     return -2;\r
4759   if (x < lineGap)\r
4760     return -1;\r
4761   x -= lineGap;\r
4762   if ((x % (squareSize + lineGap)) >= squareSize)\r
4763     return -1;\r
4764   x /= (squareSize + lineGap);\r
4765   if (x >= BOARD_SIZE)\r
4766     return -2;\r
4767   return x;\r
4768 }\r
4769 \r
4770 typedef struct {\r
4771   char piece;\r
4772   int command;\r
4773   char* name;\r
4774 } DropEnable;\r
4775 \r
4776 DropEnable dropEnables[] = {\r
4777   { 'P', DP_Pawn, "Pawn" },\r
4778   { 'N', DP_Knight, "Knight" },\r
4779   { 'B', DP_Bishop, "Bishop" },\r
4780   { 'R', DP_Rook, "Rook" },\r
4781   { 'Q', DP_Queen, "Queen" },\r
4782 };\r
4783 \r
4784 VOID\r
4785 SetupDropMenu(HMENU hmenu)\r
4786 {\r
4787   int i, count, enable;\r
4788   char *p;\r
4789   extern char white_holding[], black_holding[];\r
4790   char item[MSG_SIZ];\r
4791 \r
4792   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4793     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4794                dropEnables[i].piece);\r
4795     count = 0;\r
4796     while (p && *p++ == dropEnables[i].piece) count++;\r
4797     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4798     enable = count > 0 || !appData.testLegality\r
4799       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4800                       && !appData.icsActive);\r
4801     ModifyMenu(hmenu, dropEnables[i].command,\r
4802                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4803                dropEnables[i].command, item);\r
4804   }\r
4805 }\r
4806 \r
4807 static int fromX = -1, fromY = -1, toX, toY;\r
4808 \r
4809 /* Event handler for mouse messages */\r
4810 VOID\r
4811 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4812 {\r
4813   int x, y;\r
4814   POINT pt;\r
4815   static int recursive = 0;\r
4816   HMENU hmenu;\r
4817 //  BOOLEAN needsRedraw = FALSE;\r
4818   BOOLEAN saveAnimate;\r
4819   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4820   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4821   ChessMove moveType;\r
4822 \r
4823   if (recursive) {\r
4824     if (message == WM_MBUTTONUP) {\r
4825       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4826          to the middle button: we simulate pressing the left button too!\r
4827          */\r
4828       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4829       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4830     }\r
4831     return;\r
4832   }\r
4833   recursive++;\r
4834   \r
4835   pt.x = LOWORD(lParam);\r
4836   pt.y = HIWORD(lParam);\r
4837   x = EventToSquare(pt.x - boardRect.left);\r
4838   y = EventToSquare(pt.y - boardRect.top);\r
4839   if (!flipView && y >= 0) {\r
4840     y = BOARD_HEIGHT - 1 - y;\r
4841   }\r
4842   if (flipView && x >= 0) {\r
4843     x = BOARD_WIDTH - 1 - x;\r
4844   }\r
4845 \r
4846   switch (message) {\r
4847   case WM_LBUTTONDOWN:\r
4848     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4849         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4850         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4851         if(gameInfo.holdingsWidth && \r
4852                 (WhiteOnMove(currentMove) \r
4853                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4854                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4855             // click in right holdings, for determining promotion piece\r
4856             ChessSquare p = boards[currentMove][y][x];\r
4857             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4858             if(p != EmptySquare) {\r
4859                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4860                 fromX = fromY = -1;\r
4861                 break;\r
4862             }\r
4863         }\r
4864         DrawPosition(FALSE, boards[currentMove]);\r
4865         break;\r
4866     }\r
4867     ErrorPopDown();\r
4868     sameAgain = FALSE;\r
4869     if (y == -2) {\r
4870       /* Downclick vertically off board; check if on clock */\r
4871       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4872         if (gameMode == EditPosition) {\r
4873           SetWhiteToPlayEvent();\r
4874         } else if (gameMode == IcsPlayingBlack ||\r
4875                    gameMode == MachinePlaysWhite) {\r
4876           CallFlagEvent();\r
4877         } else if (gameMode == EditGame) {\r
4878           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4879         }\r
4880       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4881         if (gameMode == EditPosition) {\r
4882           SetBlackToPlayEvent();\r
4883         } else if (gameMode == IcsPlayingWhite ||\r
4884                    gameMode == MachinePlaysBlack) {\r
4885           CallFlagEvent();\r
4886         } else if (gameMode == EditGame) {\r
4887           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4888         }\r
4889       }\r
4890       if (!appData.highlightLastMove) {\r
4891         ClearHighlights();\r
4892         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4893       }\r
4894       fromX = fromY = -1;\r
4895       dragInfo.start.x = dragInfo.start.y = -1;\r
4896       dragInfo.from = dragInfo.start;\r
4897       break;\r
4898     } else if (x < 0 || y < 0\r
4899       /* [HGM] block clicks between board and holdings */\r
4900               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4901               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4902               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4903         /* EditPosition, empty square, or different color piece;\r
4904            click-click move is possible */\r
4905                                ) {\r
4906       break;\r
4907     } else if (fromX == x && fromY == y) {\r
4908       /* Downclick on same square again */\r
4909       ClearHighlights();\r
4910       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4911       sameAgain = TRUE;  \r
4912     } else if (fromX != -1 &&\r
4913                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4914                                                                         ) {\r
4915       /* Downclick on different square. */\r
4916       /* [HGM] if on holdings file, should count as new first click ! */\r
4917       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4918         toX = x;\r
4919         toY = y;\r
4920         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4921            to make sure move is legal before showing promotion popup */\r
4922         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4923         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4924                 fromX = fromY = -1; \r
4925                 ClearHighlights();\r
4926                 DrawPosition(FALSE, boards[currentMove]);\r
4927                 break; \r
4928         } else \r
4929         if(moveType != ImpossibleMove) {\r
4930           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4931           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4932             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4933               appData.alwaysPromoteToQueen)) {\r
4934                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4935                   if (!appData.highlightLastMove) {\r
4936                       ClearHighlights();\r
4937                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4938                   }\r
4939           } else\r
4940           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4941                   SetHighlights(fromX, fromY, toX, toY);\r
4942                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4943                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4944                      If promotion to Q is legal, all are legal! */\r
4945                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4946                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4947                     // kludge to temporarily execute move on display, wthout promotng yet\r
4948                     promotionChoice = TRUE;\r
4949                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4950                     boards[currentMove][toY][toX] = p;\r
4951                     DrawPosition(FALSE, boards[currentMove]);\r
4952                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4953                     boards[currentMove][toY][toX] = q;\r
4954                   } else\r
4955                   PromotionPopup(hwnd);\r
4956           } else {       /* not a promotion */\r
4957              if (appData.animate || appData.highlightLastMove) {\r
4958                  SetHighlights(fromX, fromY, toX, toY);\r
4959              } else {\r
4960                  ClearHighlights();\r
4961              }\r
4962              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4963              fromX = fromY = -1;\r
4964              if (appData.animate && !appData.highlightLastMove) {\r
4965                   ClearHighlights();\r
4966                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4967              }\r
4968           }\r
4969           break;\r
4970         }\r
4971         if (gotPremove) {\r
4972             /* [HGM] it seemed that braces were missing here */\r
4973             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4974             fromX = fromY = -1;\r
4975             break;\r
4976         }\r
4977       }\r
4978       ClearHighlights();\r
4979       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4980     }\r
4981     /* First downclick, or restart on a square with same color piece */\r
4982     if (!frozen && OKToStartUserMove(x, y)) {\r
4983       fromX = x;\r
4984       fromY = y;\r
4985       dragInfo.lastpos = pt;\r
4986       dragInfo.from.x = fromX;\r
4987       dragInfo.from.y = fromY;\r
4988       dragInfo.start = dragInfo.from;\r
4989       SetCapture(hwndMain);\r
4990     } else {\r
4991       fromX = fromY = -1;\r
4992       dragInfo.start.x = dragInfo.start.y = -1;\r
4993       dragInfo.from = dragInfo.start;\r
4994       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4995     }\r
4996     break;\r
4997 \r
4998   case WM_LBUTTONUP:\r
4999     ReleaseCapture();\r
5000     if (fromX == -1) break;\r
5001     if (x == fromX && y == fromY) {\r
5002       dragInfo.from.x = dragInfo.from.y = -1;\r
5003       /* Upclick on same square */\r
5004       if (sameAgain) {\r
5005         /* Clicked same square twice: abort click-click move */\r
5006         fromX = fromY = -1;\r
5007         gotPremove = 0;\r
5008         ClearPremoveHighlights();\r
5009       } else {\r
5010         /* First square clicked: start click-click move */\r
5011         SetHighlights(fromX, fromY, -1, -1);\r
5012       }\r
5013       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5014     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5015       /* Errant click; ignore */\r
5016       break;\r
5017     } else {\r
5018       /* Finish drag move. */\r
5019     if (appData.debugMode) {\r
5020         fprintf(debugFP, "release\n");\r
5021     }\r
5022       dragInfo.from.x = dragInfo.from.y = -1;\r
5023       toX = x;\r
5024       toY = y;\r
5025       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5026       appData.animate = appData.animate && !appData.animateDragging;\r
5027       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5028       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5029                 fromX = fromY = -1; \r
5030                 ClearHighlights();\r
5031                 DrawPosition(FALSE, boards[currentMove]);\r
5032                 break; \r
5033       } else \r
5034       if(moveType != ImpossibleMove) {\r
5035           /* [HGM] use move type to determine if move is promotion.\r
5036              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5037           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5038             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5039               appData.alwaysPromoteToQueen)) \r
5040                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5041           else \r
5042           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5043                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5044                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5045                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5046                     // kludge to temporarily execute move on display, wthout promotng yet\r
5047                     promotionChoice = TRUE;\r
5048                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5049                     boards[currentMove][toY][toX] = p;\r
5050                     DrawPosition(FALSE, boards[currentMove]);\r
5051                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5052                     boards[currentMove][toY][toX] = q;\r
5053                     break;\r
5054                   } else\r
5055                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5056           } else {\r
5057             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5058                         && boards[currentMove][toY][toX] != EmptySquare) AnimateAtomicCapture(toX, toY, 20);\r
5059             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5060           }\r
5061       }\r
5062       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5063       appData.animate = saveAnimate;\r
5064       fromX = fromY = -1;\r
5065       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5066         ClearHighlights();\r
5067       }\r
5068       if (appData.animate || appData.animateDragging ||\r
5069           appData.highlightDragging || gotPremove) {\r
5070         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5071       }\r
5072     }\r
5073     dragInfo.start.x = dragInfo.start.y = -1; \r
5074     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5075     break;\r
5076 \r
5077   case WM_MOUSEMOVE:\r
5078     if ((appData.animateDragging || appData.highlightDragging)\r
5079         && (wParam & MK_LBUTTON)\r
5080         && dragInfo.from.x >= 0) \r
5081     {\r
5082       BOOL full_repaint = FALSE;\r
5083 \r
5084       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5085       if (appData.animateDragging) {\r
5086         dragInfo.pos = pt;\r
5087       }\r
5088       if (appData.highlightDragging) {\r
5089         SetHighlights(fromX, fromY, x, y);\r
5090         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5091             full_repaint = TRUE;\r
5092         }\r
5093       }\r
5094       \r
5095       DrawPosition( full_repaint, NULL);\r
5096       \r
5097       dragInfo.lastpos = dragInfo.pos;\r
5098     }\r
5099     break;\r
5100 \r
5101   case WM_MOUSEWHEEL: // [DM]\r
5102     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5103        /* Mouse Wheel is being rolled forward\r
5104         * Play moves forward\r
5105         */\r
5106        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5107                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5108        /* Mouse Wheel is being rolled backward\r
5109         * Play moves backward\r
5110         */\r
5111        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5112                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5113     }\r
5114     break;\r
5115 \r
5116   case WM_MBUTTONDOWN:\r
5117   case WM_RBUTTONDOWN:\r
5118     ErrorPopDown();\r
5119     ReleaseCapture();\r
5120     fromX = fromY = -1;\r
5121     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5122     dragInfo.start.x = dragInfo.start.y = -1;\r
5123     dragInfo.from = dragInfo.start;\r
5124     dragInfo.lastpos = dragInfo.pos;\r
5125     if (appData.highlightDragging) {\r
5126       ClearHighlights();\r
5127     }\r
5128     if(y == -2) {\r
5129       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5130       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5131           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5132       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5133           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5134       }\r
5135     }\r
5136     DrawPosition(TRUE, NULL);\r
5137 \r
5138     switch (gameMode) {\r
5139     case EditPosition:\r
5140     case IcsExamining:\r
5141       if (x < 0 || y < 0) break;\r
5142       fromX = x;\r
5143       fromY = y;\r
5144       if (message == WM_MBUTTONDOWN) {\r
5145         buttonCount = 3;  /* even if system didn't think so */\r
5146         if (wParam & MK_SHIFT) \r
5147           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5148         else\r
5149           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5150       } else { /* message == WM_RBUTTONDOWN */\r
5151 #if 0\r
5152         if (buttonCount == 3) {\r
5153           if (wParam & MK_SHIFT) \r
5154             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5155           else\r
5156             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5157         } else {\r
5158           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5159         }\r
5160 #else\r
5161         /* Just have one menu, on the right button.  Windows users don't\r
5162            think to try the middle one, and sometimes other software steals\r
5163            it, or it doesn't really exist. */\r
5164         if(gameInfo.variant != VariantShogi)\r
5165             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5166         else\r
5167             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5168 #endif\r
5169       }\r
5170       break;\r
5171     case IcsPlayingWhite:\r
5172     case IcsPlayingBlack:\r
5173     case EditGame:\r
5174     case MachinePlaysWhite:\r
5175     case MachinePlaysBlack:\r
5176       if (appData.testLegality &&\r
5177           gameInfo.variant != VariantBughouse &&\r
5178           gameInfo.variant != VariantCrazyhouse) break;\r
5179       if (x < 0 || y < 0) break;\r
5180       fromX = x;\r
5181       fromY = y;\r
5182       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5183       SetupDropMenu(hmenu);\r
5184       MenuPopup(hwnd, pt, hmenu, -1);\r
5185       break;\r
5186     default:\r
5187       break;\r
5188     }\r
5189     break;\r
5190   }\r
5191 \r
5192   recursive--;\r
5193 }\r
5194 \r
5195 /* Preprocess messages for buttons in main window */\r
5196 LRESULT CALLBACK\r
5197 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5198 {\r
5199   int id = GetWindowLong(hwnd, GWL_ID);\r
5200   int i, dir;\r
5201 \r
5202   for (i=0; i<N_BUTTONS; i++) {\r
5203     if (buttonDesc[i].id == id) break;\r
5204   }\r
5205   if (i == N_BUTTONS) return 0;\r
5206   switch (message) {\r
5207   case WM_KEYDOWN:\r
5208     switch (wParam) {\r
5209     case VK_LEFT:\r
5210     case VK_RIGHT:\r
5211       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5212       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5213       return TRUE;\r
5214     }\r
5215     break;\r
5216   case WM_CHAR:\r
5217     switch (wParam) {\r
5218     case '\r':\r
5219       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5220       return TRUE;\r
5221     case '\t':\r
5222       if (appData.icsActive) {\r
5223         if (GetKeyState(VK_SHIFT) < 0) {\r
5224           /* shifted */\r
5225           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5226           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5227           SetFocus(h);\r
5228         } else {\r
5229           /* unshifted */\r
5230           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5231           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5232           SetFocus(h);\r
5233         }\r
5234         return TRUE;\r
5235       }\r
5236       break;\r
5237     default:\r
5238       if (appData.icsActive) {\r
5239         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5240         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5241         SetFocus(h);\r
5242         SendMessage(h, WM_CHAR, wParam, lParam);\r
5243         return TRUE;\r
5244       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5245         PopUpMoveDialog((char)wParam);\r
5246       }\r
5247       break;\r
5248     }\r
5249     break;\r
5250   }\r
5251   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5252 }\r
5253 \r
5254 /* Process messages for Promotion dialog box */\r
5255 LRESULT CALLBACK\r
5256 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5257 {\r
5258   char promoChar;\r
5259 \r
5260   switch (message) {\r
5261   case WM_INITDIALOG: /* message: initialize dialog box */\r
5262     /* Center the dialog over the application window */\r
5263     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5264     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5265       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5266        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5267                SW_SHOW : SW_HIDE);\r
5268     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5269     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5270        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5271          PieceToChar(WhiteAngel) != '~') ||\r
5272         (PieceToChar(BlackAngel) >= 'A' &&\r
5273          PieceToChar(BlackAngel) != '~')   ) ?\r
5274                SW_SHOW : SW_HIDE);\r
5275     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5276        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5277          PieceToChar(WhiteMarshall) != '~') ||\r
5278         (PieceToChar(BlackMarshall) >= 'A' &&\r
5279          PieceToChar(BlackMarshall) != '~')   ) ?\r
5280                SW_SHOW : SW_HIDE);\r
5281     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5282     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5283        gameInfo.variant != VariantShogi ?\r
5284                SW_SHOW : SW_HIDE);\r
5285     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5286        gameInfo.variant != VariantShogi ?\r
5287                SW_SHOW : SW_HIDE);\r
5288     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5289        gameInfo.variant == VariantShogi ?\r
5290                SW_SHOW : SW_HIDE);\r
5291     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5292        gameInfo.variant == VariantShogi ?\r
5293                SW_SHOW : SW_HIDE);\r
5294     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5295        gameInfo.variant == VariantSuper ?\r
5296                SW_SHOW : SW_HIDE);\r
5297     return TRUE;\r
5298 \r
5299   case WM_COMMAND: /* message: received a command */\r
5300     switch (LOWORD(wParam)) {\r
5301     case IDCANCEL:\r
5302       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5303       ClearHighlights();\r
5304       DrawPosition(FALSE, NULL);\r
5305       return TRUE;\r
5306     case PB_King:\r
5307       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5308       break;\r
5309     case PB_Queen:\r
5310       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5311       break;\r
5312     case PB_Rook:\r
5313       promoChar = PieceToChar(BlackRook);\r
5314       break;\r
5315     case PB_Bishop:\r
5316       promoChar = PieceToChar(BlackBishop);\r
5317       break;\r
5318     case PB_Chancellor:\r
5319       promoChar = PieceToChar(BlackMarshall);\r
5320       break;\r
5321     case PB_Archbishop:\r
5322       promoChar = PieceToChar(BlackAngel);\r
5323       break;\r
5324     case PB_Knight:\r
5325       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5326       break;\r
5327     default:\r
5328       return FALSE;\r
5329     }\r
5330     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5331     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5332        only show the popup when we are already sure the move is valid or\r
5333        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5334        will figure out it is a promotion from the promoChar. */\r
5335     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5336     if (!appData.highlightLastMove) {\r
5337       ClearHighlights();\r
5338       DrawPosition(FALSE, NULL);\r
5339     }\r
5340     return TRUE;\r
5341   }\r
5342   return FALSE;\r
5343 }\r
5344 \r
5345 /* Pop up promotion dialog */\r
5346 VOID\r
5347 PromotionPopup(HWND hwnd)\r
5348 {\r
5349   FARPROC lpProc;\r
5350 \r
5351   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5352   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5353     hwnd, (DLGPROC)lpProc);\r
5354   FreeProcInstance(lpProc);\r
5355 }\r
5356 \r
5357 /* Toggle ShowThinking */\r
5358 VOID\r
5359 ToggleShowThinking()\r
5360 {\r
5361   appData.showThinking = !appData.showThinking;\r
5362   ShowThinkingEvent();\r
5363 }\r
5364 \r
5365 VOID\r
5366 LoadGameDialog(HWND hwnd, char* title)\r
5367 {\r
5368   UINT number = 0;\r
5369   FILE *f;\r
5370   char fileTitle[MSG_SIZ];\r
5371   f = OpenFileDialog(hwnd, "rb", "",\r
5372                      appData.oldSaveStyle ? "gam" : "pgn",\r
5373                      GAME_FILT,\r
5374                      title, &number, fileTitle, NULL);\r
5375   if (f != NULL) {\r
5376     cmailMsgLoaded = FALSE;\r
5377     if (number == 0) {\r
5378       int error = GameListBuild(f);\r
5379       if (error) {\r
5380         DisplayError("Cannot build game list", error);\r
5381       } else if (!ListEmpty(&gameList) &&\r
5382                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5383         GameListPopUp(f, fileTitle);\r
5384         return;\r
5385       }\r
5386       GameListDestroy();\r
5387       number = 1;\r
5388     }\r
5389     LoadGame(f, number, fileTitle, FALSE);\r
5390   }\r
5391 }\r
5392 \r
5393 VOID\r
5394 ChangedConsoleFont()\r
5395 {\r
5396   CHARFORMAT cfmt;\r
5397   CHARRANGE tmpsel, sel;\r
5398   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5399   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5400   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5401   PARAFORMAT paraf;\r
5402 \r
5403   cfmt.cbSize = sizeof(CHARFORMAT);\r
5404   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5405   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5406   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5407    * size.  This was undocumented in the version of MSVC++ that I had\r
5408    * when I wrote the code, but is apparently documented now.\r
5409    */\r
5410   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5411   cfmt.bCharSet = f->lf.lfCharSet;\r
5412   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5413   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5414   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5415   /* Why are the following seemingly needed too? */\r
5416   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5417   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5418   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5419   tmpsel.cpMin = 0;\r
5420   tmpsel.cpMax = -1; /*999999?*/\r
5421   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5422   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5423   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5424    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5425    */\r
5426   paraf.cbSize = sizeof(paraf);\r
5427   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5428   paraf.dxStartIndent = 0;\r
5429   paraf.dxOffset = WRAP_INDENT;\r
5430   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5431   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5432 }\r
5433 \r
5434 /*---------------------------------------------------------------------------*\\r
5435  *\r
5436  * Window Proc for main window\r
5437  *\r
5438 \*---------------------------------------------------------------------------*/\r
5439 \r
5440 /* Process messages for main window, etc. */\r
5441 LRESULT CALLBACK\r
5442 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5443 {\r
5444   FARPROC lpProc;\r
5445   int wmId, wmEvent;\r
5446   char *defName;\r
5447   FILE *f;\r
5448   UINT number;\r
5449   char fileTitle[MSG_SIZ];\r
5450   char buf[MSG_SIZ];\r
5451   static SnapData sd;\r
5452 \r
5453   switch (message) {\r
5454 \r
5455   case WM_PAINT: /* message: repaint portion of window */\r
5456     PaintProc(hwnd);\r
5457     break;\r
5458 \r
5459   case WM_ERASEBKGND:\r
5460     if (IsIconic(hwnd)) {\r
5461       /* Cheat; change the message */\r
5462       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5463     } else {\r
5464       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5465     }\r
5466     break;\r
5467 \r
5468   case WM_LBUTTONDOWN:\r
5469   case WM_MBUTTONDOWN:\r
5470   case WM_RBUTTONDOWN:\r
5471   case WM_LBUTTONUP:\r
5472   case WM_MBUTTONUP:\r
5473   case WM_RBUTTONUP:\r
5474   case WM_MOUSEMOVE:\r
5475   case WM_MOUSEWHEEL:\r
5476     MouseEvent(hwnd, message, wParam, lParam);\r
5477     break;\r
5478 \r
5479   case WM_CHAR:\r
5480     \r
5481     if (appData.icsActive) {\r
5482       if (wParam == '\t') {\r
5483         if (GetKeyState(VK_SHIFT) < 0) {\r
5484           /* shifted */\r
5485           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5486           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5487           SetFocus(h);\r
5488         } else {\r
5489           /* unshifted */\r
5490           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5491           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5492           SetFocus(h);\r
5493         }\r
5494       } else {\r
5495         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5496         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5497         SetFocus(h);\r
5498         SendMessage(h, message, wParam, lParam);\r
5499       }\r
5500     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5501       PopUpMoveDialog((char)wParam);\r
5502     }\r
5503     break;\r
5504 \r
5505   case WM_PALETTECHANGED:\r
5506     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5507       int nnew;\r
5508       HDC hdc = GetDC(hwndMain);\r
5509       SelectPalette(hdc, hPal, TRUE);\r
5510       nnew = RealizePalette(hdc);\r
5511       if (nnew > 0) {\r
5512         paletteChanged = TRUE;\r
5513 #if 0\r
5514         UpdateColors(hdc);\r
5515 #else\r
5516         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5517 #endif\r
5518       }\r
5519       ReleaseDC(hwnd, hdc);\r
5520     }\r
5521     break;\r
5522 \r
5523   case WM_QUERYNEWPALETTE:\r
5524     if (!appData.monoMode /*&& paletteChanged*/) {\r
5525       int nnew;\r
5526       HDC hdc = GetDC(hwndMain);\r
5527       paletteChanged = FALSE;\r
5528       SelectPalette(hdc, hPal, FALSE);\r
5529       nnew = RealizePalette(hdc);\r
5530       if (nnew > 0) {\r
5531         InvalidateRect(hwnd, &boardRect, FALSE);\r
5532       }\r
5533       ReleaseDC(hwnd, hdc);\r
5534       return TRUE;\r
5535     }\r
5536     return FALSE;\r
5537 \r
5538   case WM_COMMAND: /* message: command from application menu */\r
5539     wmId    = LOWORD(wParam);\r
5540     wmEvent = HIWORD(wParam);\r
5541 \r
5542     switch (wmId) {\r
5543     case IDM_NewGame:\r
5544       ResetGameEvent();\r
5545       AnalysisPopDown();\r
5546       break;\r
5547 \r
5548     case IDM_NewGameFRC:\r
5549       if( NewGameFRC() == 0 ) {\r
5550         ResetGameEvent();\r
5551         AnalysisPopDown();\r
5552       }\r
5553       break;\r
5554 \r
5555     case IDM_NewVariant:\r
5556       NewVariantPopup(hwnd);\r
5557       break;\r
5558 \r
5559     case IDM_LoadGame:\r
5560       LoadGameDialog(hwnd, "Load Game from File");\r
5561       break;\r
5562 \r
5563     case IDM_LoadNextGame:\r
5564       ReloadGame(1);\r
5565       break;\r
5566 \r
5567     case IDM_LoadPrevGame:\r
5568       ReloadGame(-1);\r
5569       break;\r
5570 \r
5571     case IDM_ReloadGame:\r
5572       ReloadGame(0);\r
5573       break;\r
5574 \r
5575     case IDM_LoadPosition:\r
5576       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5577         Reset(FALSE, TRUE);\r
5578       }\r
5579       number = 1;\r
5580       f = OpenFileDialog(hwnd, "rb", "",\r
5581                          appData.oldSaveStyle ? "pos" : "fen",\r
5582                          POSITION_FILT,\r
5583                          "Load Position from File", &number, fileTitle, NULL);\r
5584       if (f != NULL) {\r
5585         LoadPosition(f, number, fileTitle);\r
5586       }\r
5587       break;\r
5588 \r
5589     case IDM_LoadNextPosition:\r
5590       ReloadPosition(1);\r
5591       break;\r
5592 \r
5593     case IDM_LoadPrevPosition:\r
5594       ReloadPosition(-1);\r
5595       break;\r
5596 \r
5597     case IDM_ReloadPosition:\r
5598       ReloadPosition(0);\r
5599       break;\r
5600 \r
5601     case IDM_SaveGame:\r
5602       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5603       f = OpenFileDialog(hwnd, "a", defName,\r
5604                          appData.oldSaveStyle ? "gam" : "pgn",\r
5605                          GAME_FILT,\r
5606                          "Save Game to File", NULL, fileTitle, NULL);\r
5607       if (f != NULL) {\r
5608         SaveGame(f, 0, "");\r
5609       }\r
5610       break;\r
5611 \r
5612     case IDM_SavePosition:\r
5613       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5614       f = OpenFileDialog(hwnd, "a", defName,\r
5615                          appData.oldSaveStyle ? "pos" : "fen",\r
5616                          POSITION_FILT,\r
5617                          "Save Position to File", NULL, fileTitle, NULL);\r
5618       if (f != NULL) {\r
5619         SavePosition(f, 0, "");\r
5620       }\r
5621       break;\r
5622 \r
5623     case IDM_SaveDiagram:\r
5624       defName = "diagram";\r
5625       f = OpenFileDialog(hwnd, "wb", defName,\r
5626                          "bmp",\r
5627                          DIAGRAM_FILT,\r
5628                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5629       if (f != NULL) {\r
5630         SaveDiagram(f);\r
5631       }\r
5632       break;\r
5633 \r
5634     case IDM_CopyGame:\r
5635       CopyGameToClipboard();\r
5636       break;\r
5637 \r
5638     case IDM_PasteGame:\r
5639       PasteGameFromClipboard();\r
5640       break;\r
5641 \r
5642     case IDM_CopyGameListToClipboard:\r
5643       CopyGameListToClipboard();\r
5644       break;\r
5645 \r
5646     /* [AS] Autodetect FEN or PGN data */\r
5647     case IDM_PasteAny:\r
5648       PasteGameOrFENFromClipboard();\r
5649       break;\r
5650 \r
5651     /* [AS] Move history */\r
5652     case IDM_ShowMoveHistory:\r
5653         if( MoveHistoryIsUp() ) {\r
5654             MoveHistoryPopDown();\r
5655         }\r
5656         else {\r
5657             MoveHistoryPopUp();\r
5658         }\r
5659         break;\r
5660 \r
5661     /* [AS] Eval graph */\r
5662     case IDM_ShowEvalGraph:\r
5663         if( EvalGraphIsUp() ) {\r
5664             EvalGraphPopDown();\r
5665         }\r
5666         else {\r
5667             EvalGraphPopUp();\r
5668         }\r
5669         break;\r
5670 \r
5671     /* [AS] Engine output */\r
5672     case IDM_ShowEngineOutput:\r
5673         if( EngineOutputIsUp() ) {\r
5674             EngineOutputPopDown();\r
5675         }\r
5676         else {\r
5677             EngineOutputPopUp();\r
5678         }\r
5679         break;\r
5680 \r
5681     /* [AS] User adjudication */\r
5682     case IDM_UserAdjudication_White:\r
5683         UserAdjudicationEvent( +1 );\r
5684         break;\r
5685 \r
5686     case IDM_UserAdjudication_Black:\r
5687         UserAdjudicationEvent( -1 );\r
5688         break;\r
5689 \r
5690     case IDM_UserAdjudication_Draw:\r
5691         UserAdjudicationEvent( 0 );\r
5692         break;\r
5693 \r
5694     /* [AS] Game list options dialog */\r
5695     case IDM_GameListOptions:\r
5696       GameListOptions();\r
5697       break;\r
5698 \r
5699     case IDM_CopyPosition:\r
5700       CopyFENToClipboard();\r
5701       break;\r
5702 \r
5703     case IDM_PastePosition:\r
5704       PasteFENFromClipboard();\r
5705       break;\r
5706 \r
5707     case IDM_MailMove:\r
5708       MailMoveEvent();\r
5709       break;\r
5710 \r
5711     case IDM_ReloadCMailMsg:\r
5712       Reset(TRUE, TRUE);\r
5713       ReloadCmailMsgEvent(FALSE);\r
5714       break;\r
5715 \r
5716     case IDM_Minimize:\r
5717       ShowWindow(hwnd, SW_MINIMIZE);\r
5718       break;\r
5719 \r
5720     case IDM_Exit:\r
5721       ExitEvent(0);\r
5722       break;\r
5723 \r
5724     case IDM_MachineWhite:\r
5725       MachineWhiteEvent();\r
5726       /*\r
5727        * refresh the tags dialog only if it's visible\r
5728        */\r
5729       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5730           char *tags;\r
5731           tags = PGNTags(&gameInfo);\r
5732           TagsPopUp(tags, CmailMsg());\r
5733           free(tags);\r
5734       }\r
5735       break;\r
5736 \r
5737     case IDM_MachineBlack:\r
5738       MachineBlackEvent();\r
5739       /*\r
5740        * refresh the tags dialog only if it's visible\r
5741        */\r
5742       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5743           char *tags;\r
5744           tags = PGNTags(&gameInfo);\r
5745           TagsPopUp(tags, CmailMsg());\r
5746           free(tags);\r
5747       }\r
5748       break;\r
5749 \r
5750     case IDM_TwoMachines:\r
5751       TwoMachinesEvent();\r
5752       /*\r
5753        * refresh the tags dialog only if it's visible\r
5754        */\r
5755       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5756           char *tags;\r
5757           tags = PGNTags(&gameInfo);\r
5758           TagsPopUp(tags, CmailMsg());\r
5759           free(tags);\r
5760       }\r
5761       break;\r
5762 \r
5763     case IDM_AnalysisMode:\r
5764       if (!first.analysisSupport) {\r
5765         sprintf(buf, "%s does not support analysis", first.tidy);\r
5766         DisplayError(buf, 0);\r
5767       } else {\r
5768         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5769         if (appData.icsActive) {\r
5770                if (gameMode != IcsObserving) {\r
5771                        sprintf(buf, "You are not observing a game");\r
5772                        DisplayError(buf, 0);\r
5773                        /* secure check */\r
5774                        if (appData.icsEngineAnalyze) {\r
5775                                if (appData.debugMode) \r
5776                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5777                                ExitAnalyzeMode();\r
5778                                ModeHighlight();\r
5779                                break;\r
5780                        }\r
5781                        break;\r
5782                } else {\r
5783                        /* if enable, user want disable icsEngineAnalyze */\r
5784                        if (appData.icsEngineAnalyze) {\r
5785                                ExitAnalyzeMode();\r
5786                                ModeHighlight();\r
5787                                break;\r
5788                        }\r
5789                        appData.icsEngineAnalyze = TRUE;\r
5790                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5791                }\r
5792         } \r
5793         if (!appData.showThinking) ToggleShowThinking();\r
5794         AnalyzeModeEvent();\r
5795       }\r
5796       break;\r
5797 \r
5798     case IDM_AnalyzeFile:\r
5799       if (!first.analysisSupport) {\r
5800         char buf[MSG_SIZ];\r
5801         sprintf(buf, "%s does not support analysis", first.tidy);\r
5802         DisplayError(buf, 0);\r
5803       } else {\r
5804         if (!appData.showThinking) ToggleShowThinking();\r
5805         AnalyzeFileEvent();\r
5806         LoadGameDialog(hwnd, "Analyze Game from File");\r
5807         AnalysisPeriodicEvent(1);\r
5808       }\r
5809       break;\r
5810 \r
5811     case IDM_IcsClient:\r
5812       IcsClientEvent();\r
5813       break;\r
5814 \r
5815     case IDM_EditGame:\r
5816       EditGameEvent();\r
5817       break;\r
5818 \r
5819     case IDM_EditPosition:\r
5820       EditPositionEvent();\r
5821       break;\r
5822 \r
5823     case IDM_Training:\r
5824       TrainingEvent();\r
5825       break;\r
5826 \r
5827     case IDM_ShowGameList:\r
5828       ShowGameListProc();\r
5829       break;\r
5830 \r
5831     case IDM_EditTags:\r
5832       EditTagsProc();\r
5833       break;\r
5834 \r
5835     case IDM_EditComment:\r
5836       if (commentDialogUp && editComment) {\r
5837         CommentPopDown();\r
5838       } else {\r
5839         EditCommentEvent();\r
5840       }\r
5841       break;\r
5842 \r
5843     case IDM_Pause:\r
5844       PauseEvent();\r
5845       break;\r
5846 \r
5847     case IDM_Accept:\r
5848       AcceptEvent();\r
5849       break;\r
5850 \r
5851     case IDM_Decline:\r
5852       DeclineEvent();\r
5853       break;\r
5854 \r
5855     case IDM_Rematch:\r
5856       RematchEvent();\r
5857       break;\r
5858 \r
5859     case IDM_CallFlag:\r
5860       CallFlagEvent();\r
5861       break;\r
5862 \r
5863     case IDM_Draw:\r
5864       DrawEvent();\r
5865       break;\r
5866 \r
5867     case IDM_Adjourn:\r
5868       AdjournEvent();\r
5869       break;\r
5870 \r
5871     case IDM_Abort:\r
5872       AbortEvent();\r
5873       break;\r
5874 \r
5875     case IDM_Resign:\r
5876       ResignEvent();\r
5877       break;\r
5878 \r
5879     case IDM_StopObserving:\r
5880       StopObservingEvent();\r
5881       break;\r
5882 \r
5883     case IDM_StopExamining:\r
5884       StopExaminingEvent();\r
5885       break;\r
5886 \r
5887     case IDM_TypeInMove:\r
5888       PopUpMoveDialog('\000');\r
5889       break;\r
5890 \r
5891     case IDM_TypeInName:\r
5892       PopUpNameDialog('\000');\r
5893       break;\r
5894 \r
5895     case IDM_Backward:\r
5896       BackwardEvent();\r
5897       SetFocus(hwndMain);\r
5898       break;\r
5899 \r
5900     case IDM_Forward:\r
5901       ForwardEvent();\r
5902       SetFocus(hwndMain);\r
5903       break;\r
5904 \r
5905     case IDM_ToStart:\r
5906       ToStartEvent();\r
5907       SetFocus(hwndMain);\r
5908       break;\r
5909 \r
5910     case IDM_ToEnd:\r
5911       ToEndEvent();\r
5912       SetFocus(hwndMain);\r
5913       break;\r
5914 \r
5915     case IDM_Revert:\r
5916       RevertEvent();\r
5917       break;\r
5918 \r
5919     case IDM_TruncateGame:\r
5920       TruncateGameEvent();\r
5921       break;\r
5922 \r
5923     case IDM_MoveNow:\r
5924       MoveNowEvent();\r
5925       break;\r
5926 \r
5927     case IDM_RetractMove:\r
5928       RetractMoveEvent();\r
5929       break;\r
5930 \r
5931     case IDM_FlipView:\r
5932       flipView = !flipView;\r
5933       DrawPosition(FALSE, NULL);\r
5934       break;\r
5935 \r
5936     case IDM_FlipClock:\r
5937       flipClock = !flipClock;\r
5938       DisplayBothClocks();\r
5939       break;\r
5940 \r
5941     case IDM_GeneralOptions:\r
5942       GeneralOptionsPopup(hwnd);\r
5943       DrawPosition(TRUE, NULL);\r
5944       break;\r
5945 \r
5946     case IDM_BoardOptions:\r
5947       BoardOptionsPopup(hwnd);\r
5948       break;\r
5949 \r
5950     case IDM_EnginePlayOptions:\r
5951       EnginePlayOptionsPopup(hwnd);\r
5952       break;\r
5953 \r
5954     case IDM_OptionsUCI:\r
5955       UciOptionsPopup(hwnd);\r
5956       break;\r
5957 \r
5958     case IDM_IcsOptions:\r
5959       IcsOptionsPopup(hwnd);\r
5960       break;\r
5961 \r
5962     case IDM_Fonts:\r
5963       FontsOptionsPopup(hwnd);\r
5964       break;\r
5965 \r
5966     case IDM_Sounds:\r
5967       SoundOptionsPopup(hwnd);\r
5968       break;\r
5969 \r
5970     case IDM_CommPort:\r
5971       CommPortOptionsPopup(hwnd);\r
5972       break;\r
5973 \r
5974     case IDM_LoadOptions:\r
5975       LoadOptionsPopup(hwnd);\r
5976       break;\r
5977 \r
5978     case IDM_SaveOptions:\r
5979       SaveOptionsPopup(hwnd);\r
5980       break;\r
5981 \r
5982     case IDM_TimeControl:\r
5983       TimeControlOptionsPopup(hwnd);\r
5984       break;\r
5985 \r
5986     case IDM_SaveSettings:\r
5987       SaveSettings(settingsFileName);\r
5988       break;\r
5989 \r
5990     case IDM_SaveSettingsOnExit:\r
5991       saveSettingsOnExit = !saveSettingsOnExit;\r
5992       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5993                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5994                                          MF_CHECKED : MF_UNCHECKED));\r
5995       break;\r
5996 \r
5997     case IDM_Hint:\r
5998       HintEvent();\r
5999       break;\r
6000 \r
6001     case IDM_Book:\r
6002       BookEvent();\r
6003       break;\r
6004 \r
6005     case IDM_AboutGame:\r
6006       AboutGameEvent();\r
6007       break;\r
6008 \r
6009     case IDM_Debug:\r
6010       appData.debugMode = !appData.debugMode;\r
6011       if (appData.debugMode) {\r
6012         char dir[MSG_SIZ];\r
6013         GetCurrentDirectory(MSG_SIZ, dir);\r
6014         SetCurrentDirectory(installDir);\r
6015         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6016         SetCurrentDirectory(dir);\r
6017         setbuf(debugFP, NULL);\r
6018       } else {\r
6019         fclose(debugFP);\r
6020         debugFP = NULL;\r
6021       }\r
6022       break;\r
6023 \r
6024     case IDM_HELPCONTENTS:\r
6025       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6026         MessageBox (GetFocus(),\r
6027                     "Unable to activate help",\r
6028                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6029       }\r
6030       break;\r
6031 \r
6032     case IDM_HELPSEARCH:\r
6033       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6034         MessageBox (GetFocus(),\r
6035                     "Unable to activate help",\r
6036                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6037       }\r
6038       break;\r
6039 \r
6040     case IDM_HELPHELP:\r
6041       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6042         MessageBox (GetFocus(),\r
6043                     "Unable to activate help",\r
6044                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6045       }\r
6046       break;\r
6047 \r
6048     case IDM_ABOUT:\r
6049       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6050       DialogBox(hInst, \r
6051         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6052         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6053       FreeProcInstance(lpProc);\r
6054       break;\r
6055 \r
6056     case IDM_DirectCommand1:\r
6057       AskQuestionEvent("Direct Command",\r
6058                        "Send to chess program:", "", "1");\r
6059       break;\r
6060     case IDM_DirectCommand2:\r
6061       AskQuestionEvent("Direct Command",\r
6062                        "Send to second chess program:", "", "2");\r
6063       break;\r
6064 \r
6065     case EP_WhitePawn:\r
6066       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6067       fromX = fromY = -1;\r
6068       break;\r
6069 \r
6070     case EP_WhiteKnight:\r
6071       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6072       fromX = fromY = -1;\r
6073       break;\r
6074 \r
6075     case EP_WhiteBishop:\r
6076       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6077       fromX = fromY = -1;\r
6078       break;\r
6079 \r
6080     case EP_WhiteRook:\r
6081       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6082       fromX = fromY = -1;\r
6083       break;\r
6084 \r
6085     case EP_WhiteQueen:\r
6086       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6087       fromX = fromY = -1;\r
6088       break;\r
6089 \r
6090     case EP_WhiteFerz:\r
6091       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6092       fromX = fromY = -1;\r
6093       break;\r
6094 \r
6095     case EP_WhiteWazir:\r
6096       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6097       fromX = fromY = -1;\r
6098       break;\r
6099 \r
6100     case EP_WhiteAlfil:\r
6101       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6102       fromX = fromY = -1;\r
6103       break;\r
6104 \r
6105     case EP_WhiteCannon:\r
6106       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6107       fromX = fromY = -1;\r
6108       break;\r
6109 \r
6110     case EP_WhiteCardinal:\r
6111       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6112       fromX = fromY = -1;\r
6113       break;\r
6114 \r
6115     case EP_WhiteMarshall:\r
6116       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6117       fromX = fromY = -1;\r
6118       break;\r
6119 \r
6120     case EP_WhiteKing:\r
6121       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6122       fromX = fromY = -1;\r
6123       break;\r
6124 \r
6125     case EP_BlackPawn:\r
6126       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6127       fromX = fromY = -1;\r
6128       break;\r
6129 \r
6130     case EP_BlackKnight:\r
6131       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6132       fromX = fromY = -1;\r
6133       break;\r
6134 \r
6135     case EP_BlackBishop:\r
6136       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6137       fromX = fromY = -1;\r
6138       break;\r
6139 \r
6140     case EP_BlackRook:\r
6141       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6142       fromX = fromY = -1;\r
6143       break;\r
6144 \r
6145     case EP_BlackQueen:\r
6146       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6147       fromX = fromY = -1;\r
6148       break;\r
6149 \r
6150     case EP_BlackFerz:\r
6151       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6152       fromX = fromY = -1;\r
6153       break;\r
6154 \r
6155     case EP_BlackWazir:\r
6156       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6157       fromX = fromY = -1;\r
6158       break;\r
6159 \r
6160     case EP_BlackAlfil:\r
6161       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6162       fromX = fromY = -1;\r
6163       break;\r
6164 \r
6165     case EP_BlackCannon:\r
6166       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6167       fromX = fromY = -1;\r
6168       break;\r
6169 \r
6170     case EP_BlackCardinal:\r
6171       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6172       fromX = fromY = -1;\r
6173       break;\r
6174 \r
6175     case EP_BlackMarshall:\r
6176       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6177       fromX = fromY = -1;\r
6178       break;\r
6179 \r
6180     case EP_BlackKing:\r
6181       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6182       fromX = fromY = -1;\r
6183       break;\r
6184 \r
6185     case EP_EmptySquare:\r
6186       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6187       fromX = fromY = -1;\r
6188       break;\r
6189 \r
6190     case EP_ClearBoard:\r
6191       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6192       fromX = fromY = -1;\r
6193       break;\r
6194 \r
6195     case EP_White:\r
6196       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6197       fromX = fromY = -1;\r
6198       break;\r
6199 \r
6200     case EP_Black:\r
6201       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6202       fromX = fromY = -1;\r
6203       break;\r
6204 \r
6205     case EP_Promote:\r
6206       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6207       fromX = fromY = -1;\r
6208       break;\r
6209 \r
6210     case EP_Demote:\r
6211       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6212       fromX = fromY = -1;\r
6213       break;\r
6214 \r
6215     case DP_Pawn:\r
6216       DropMenuEvent(WhitePawn, fromX, fromY);\r
6217       fromX = fromY = -1;\r
6218       break;\r
6219 \r
6220     case DP_Knight:\r
6221       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6222       fromX = fromY = -1;\r
6223       break;\r
6224 \r
6225     case DP_Bishop:\r
6226       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6227       fromX = fromY = -1;\r
6228       break;\r
6229 \r
6230     case DP_Rook:\r
6231       DropMenuEvent(WhiteRook, fromX, fromY);\r
6232       fromX = fromY = -1;\r
6233       break;\r
6234 \r
6235     case DP_Queen:\r
6236       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6237       fromX = fromY = -1;\r
6238       break;\r
6239 \r
6240     default:\r
6241       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6242     }\r
6243     break;\r
6244 \r
6245   case WM_TIMER:\r
6246     switch (wParam) {\r
6247     case CLOCK_TIMER_ID:\r
6248       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6249       clockTimerEvent = 0;\r
6250       DecrementClocks(); /* call into back end */\r
6251       break;\r
6252     case LOAD_GAME_TIMER_ID:\r
6253       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6254       loadGameTimerEvent = 0;\r
6255       AutoPlayGameLoop(); /* call into back end */\r
6256       break;\r
6257     case ANALYSIS_TIMER_ID:\r
6258       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6259                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6260         AnalysisPeriodicEvent(0);\r
6261       } else {\r
6262         KillTimer(hwnd, analysisTimerEvent);\r
6263         analysisTimerEvent = 0;\r
6264       }\r
6265       break;\r
6266     case DELAYED_TIMER_ID:\r
6267       KillTimer(hwnd, delayedTimerEvent);\r
6268       delayedTimerEvent = 0;\r
6269       delayedTimerCallback();\r
6270       break;\r
6271     }\r
6272     break;\r
6273 \r
6274   case WM_USER_Input:\r
6275     InputEvent(hwnd, message, wParam, lParam);\r
6276     break;\r
6277 \r
6278   /* [AS] Also move "attached" child windows */\r
6279   case WM_WINDOWPOSCHANGING:\r
6280 \r
6281     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6282         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6283 \r
6284         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6285             /* Window is moving */\r
6286             RECT rcMain;\r
6287 \r
6288 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6289             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6290             rcMain.right  = boardX + winWidth;\r
6291             rcMain.top    = boardY;\r
6292             rcMain.bottom = boardY + winHeight;\r
6293             \r
6294             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6295             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6296             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6297             boardX = lpwp->x;\r
6298             boardY = lpwp->y;\r
6299         }\r
6300     }\r
6301     break;\r
6302 \r
6303   /* [AS] Snapping */\r
6304   case WM_ENTERSIZEMOVE:\r
6305     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6306     if (hwnd == hwndMain) {\r
6307       doingSizing = TRUE;\r
6308       lastSizing = 0;\r
6309     }\r
6310     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6311     break;\r
6312 \r
6313   case WM_SIZING:\r
6314     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6315     if (hwnd == hwndMain) {\r
6316       lastSizing = wParam;\r
6317     }\r
6318     break;\r
6319 \r
6320   case WM_MOVING:\r
6321     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6322       return OnMoving( &sd, hwnd, wParam, lParam );\r
6323 \r
6324   case WM_EXITSIZEMOVE:\r
6325     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6326     if (hwnd == hwndMain) {\r
6327       RECT client;\r
6328       doingSizing = FALSE;\r
6329       InvalidateRect(hwnd, &boardRect, FALSE);\r
6330       GetClientRect(hwnd, &client);\r
6331       ResizeBoard(client.right, client.bottom, lastSizing);\r
6332       lastSizing = 0;\r
6333       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6334     }\r
6335     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6336     break;\r
6337 \r
6338   case WM_DESTROY: /* message: window being destroyed */\r
6339     PostQuitMessage(0);\r
6340     break;\r
6341 \r
6342   case WM_CLOSE:\r
6343     if (hwnd == hwndMain) {\r
6344       ExitEvent(0);\r
6345     }\r
6346     break;\r
6347 \r
6348   default:      /* Passes it on if unprocessed */\r
6349     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6350   }\r
6351   return 0;\r
6352 }\r
6353 \r
6354 /*---------------------------------------------------------------------------*\\r
6355  *\r
6356  * Misc utility routines\r
6357  *\r
6358 \*---------------------------------------------------------------------------*/\r
6359 \r
6360 /*\r
6361  * Decent random number generator, at least not as bad as Windows\r
6362  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6363  */\r
6364 unsigned int randstate;\r
6365 \r
6366 int\r
6367 myrandom(void)\r
6368 {\r
6369   randstate = randstate * 1664525 + 1013904223;\r
6370   return (int) randstate & 0x7fffffff;\r
6371 }\r
6372 \r
6373 void\r
6374 mysrandom(unsigned int seed)\r
6375 {\r
6376   randstate = seed;\r
6377 }\r
6378 \r
6379 \r
6380 /* \r
6381  * returns TRUE if user selects a different color, FALSE otherwise \r
6382  */\r
6383 \r
6384 BOOL\r
6385 ChangeColor(HWND hwnd, COLORREF *which)\r
6386 {\r
6387   static BOOL firstTime = TRUE;\r
6388   static DWORD customColors[16];\r
6389   CHOOSECOLOR cc;\r
6390   COLORREF newcolor;\r
6391   int i;\r
6392   ColorClass ccl;\r
6393 \r
6394   if (firstTime) {\r
6395     /* Make initial colors in use available as custom colors */\r
6396     /* Should we put the compiled-in defaults here instead? */\r
6397     i = 0;\r
6398     customColors[i++] = lightSquareColor & 0xffffff;\r
6399     customColors[i++] = darkSquareColor & 0xffffff;\r
6400     customColors[i++] = whitePieceColor & 0xffffff;\r
6401     customColors[i++] = blackPieceColor & 0xffffff;\r
6402     customColors[i++] = highlightSquareColor & 0xffffff;\r
6403     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6404 \r
6405     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6406       customColors[i++] = textAttribs[ccl].color;\r
6407     }\r
6408     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6409     firstTime = FALSE;\r
6410   }\r
6411 \r
6412   cc.lStructSize = sizeof(cc);\r
6413   cc.hwndOwner = hwnd;\r
6414   cc.hInstance = NULL;\r
6415   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6416   cc.lpCustColors = (LPDWORD) customColors;\r
6417   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6418 \r
6419   if (!ChooseColor(&cc)) return FALSE;\r
6420 \r
6421   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6422   if (newcolor == *which) return FALSE;\r
6423   *which = newcolor;\r
6424   return TRUE;\r
6425 \r
6426   /*\r
6427   InitDrawingColors();\r
6428   InvalidateRect(hwnd, &boardRect, FALSE);\r
6429   */\r
6430 }\r
6431 \r
6432 BOOLEAN\r
6433 MyLoadSound(MySound *ms)\r
6434 {\r
6435   BOOL ok = FALSE;\r
6436   struct stat st;\r
6437   FILE *f;\r
6438 \r
6439   if (ms->data) free(ms->data);\r
6440   ms->data = NULL;\r
6441 \r
6442   switch (ms->name[0]) {\r
6443   case NULLCHAR:\r
6444     /* Silence */\r
6445     ok = TRUE;\r
6446     break;\r
6447   case '$':\r
6448     /* System sound from Control Panel.  Don't preload here. */\r
6449     ok = TRUE;\r
6450     break;\r
6451   case '!':\r
6452     if (ms->name[1] == NULLCHAR) {\r
6453       /* "!" alone = silence */\r
6454       ok = TRUE;\r
6455     } else {\r
6456       /* Builtin wave resource.  Error if not found. */\r
6457       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6458       if (h == NULL) break;\r
6459       ms->data = (void *)LoadResource(hInst, h);\r
6460       if (h == NULL) break;\r
6461       ok = TRUE;\r
6462     }\r
6463     break;\r
6464   default:\r
6465     /* .wav file.  Error if not found. */\r
6466     f = fopen(ms->name, "rb");\r
6467     if (f == NULL) break;\r
6468     if (fstat(fileno(f), &st) < 0) break;\r
6469     ms->data = malloc(st.st_size);\r
6470     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6471     fclose(f);\r
6472     ok = TRUE;\r
6473     break;\r
6474   }\r
6475   if (!ok) {\r
6476     char buf[MSG_SIZ];\r
6477     sprintf(buf, "Error loading sound %s", ms->name);\r
6478     DisplayError(buf, GetLastError());\r
6479   }\r
6480   return ok;\r
6481 }\r
6482 \r
6483 BOOLEAN\r
6484 MyPlaySound(MySound *ms)\r
6485 {\r
6486   BOOLEAN ok = FALSE;\r
6487   switch (ms->name[0]) {\r
6488   case NULLCHAR:\r
6489     /* Silence */\r
6490     ok = TRUE;\r
6491     break;\r
6492   case '$':\r
6493     /* System sound from Control Panel (deprecated feature).\r
6494        "$" alone or an unset sound name gets default beep (still in use). */\r
6495     if (ms->name[1]) {\r
6496       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6497     }\r
6498     if (!ok) ok = MessageBeep(MB_OK);\r
6499     break; \r
6500   case '!':\r
6501     /* Builtin wave resource, or "!" alone for silence */\r
6502     if (ms->name[1]) {\r
6503       if (ms->data == NULL) return FALSE;\r
6504       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6505     } else {\r
6506       ok = TRUE;\r
6507     }\r
6508     break;\r
6509   default:\r
6510     /* .wav file.  Error if not found. */\r
6511     if (ms->data == NULL) return FALSE;\r
6512     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6513     break;\r
6514   }\r
6515   /* Don't print an error: this can happen innocently if the sound driver\r
6516      is busy; for instance, if another instance of WinBoard is playing\r
6517      a sound at about the same time. */\r
6518 #if 0\r
6519   if (!ok) {\r
6520     char buf[MSG_SIZ];\r
6521     sprintf(buf, "Error playing sound %s", ms->name);\r
6522     DisplayError(buf, GetLastError());\r
6523   }\r
6524 #endif\r
6525   return ok;\r
6526 }\r
6527 \r
6528 \r
6529 LRESULT CALLBACK\r
6530 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6531 {\r
6532   BOOL ok;\r
6533   OPENFILENAME *ofn;\r
6534   static UINT *number; /* gross that this is static */\r
6535 \r
6536   switch (message) {\r
6537   case WM_INITDIALOG: /* message: initialize dialog box */\r
6538     /* Center the dialog over the application window */\r
6539     ofn = (OPENFILENAME *) lParam;\r
6540     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6541       number = (UINT *) ofn->lCustData;\r
6542       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6543     } else {\r
6544       number = NULL;\r
6545     }\r
6546     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6547     return FALSE;  /* Allow for further processing */\r
6548 \r
6549   case WM_COMMAND:\r
6550     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6551       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6552     }\r
6553     return FALSE;  /* Allow for further processing */\r
6554   }\r
6555   return FALSE;\r
6556 }\r
6557 \r
6558 UINT APIENTRY\r
6559 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6560 {\r
6561   static UINT *number;\r
6562   OPENFILENAME *ofname;\r
6563   OFNOTIFY *ofnot;\r
6564   switch (uiMsg) {\r
6565   case WM_INITDIALOG:\r
6566     ofname = (OPENFILENAME *)lParam;\r
6567     number = (UINT *)(ofname->lCustData);\r
6568     break;\r
6569   case WM_NOTIFY:\r
6570     ofnot = (OFNOTIFY *)lParam;\r
6571     if (ofnot->hdr.code == CDN_FILEOK) {\r
6572       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6573     }\r
6574     break;\r
6575   }\r
6576   return 0;\r
6577 }\r
6578 \r
6579 \r
6580 FILE *\r
6581 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6582                char *nameFilt, char *dlgTitle, UINT *number,\r
6583                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6584 {\r
6585   OPENFILENAME openFileName;\r
6586   char buf1[MSG_SIZ];\r
6587   FILE *f;\r
6588 \r
6589   if (fileName == NULL) fileName = buf1;\r
6590   if (defName == NULL) {\r
6591     strcpy(fileName, "*.");\r
6592     strcat(fileName, defExt);\r
6593   } else {\r
6594     strcpy(fileName, defName);\r
6595   }\r
6596   if (fileTitle) strcpy(fileTitle, "");\r
6597   if (number) *number = 0;\r
6598 \r
6599   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6600   openFileName.hwndOwner         = hwnd;\r
6601   openFileName.hInstance         = (HANDLE) hInst;\r
6602   openFileName.lpstrFilter       = nameFilt;\r
6603   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6604   openFileName.nMaxCustFilter    = 0L;\r
6605   openFileName.nFilterIndex      = 1L;\r
6606   openFileName.lpstrFile         = fileName;\r
6607   openFileName.nMaxFile          = MSG_SIZ;\r
6608   openFileName.lpstrFileTitle    = fileTitle;\r
6609   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6610   openFileName.lpstrInitialDir   = NULL;\r
6611   openFileName.lpstrTitle        = dlgTitle;\r
6612   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6613     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6614     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6615     | (oldDialog ? 0 : OFN_EXPLORER);\r
6616   openFileName.nFileOffset       = 0;\r
6617   openFileName.nFileExtension    = 0;\r
6618   openFileName.lpstrDefExt       = defExt;\r
6619   openFileName.lCustData         = (LONG) number;\r
6620   openFileName.lpfnHook          = oldDialog ?\r
6621     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6622   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6623 \r
6624   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6625                         GetOpenFileName(&openFileName)) {\r
6626     /* open the file */\r
6627     f = fopen(openFileName.lpstrFile, write);\r
6628     if (f == NULL) {\r
6629       MessageBox(hwnd, "File open failed", NULL,\r
6630                  MB_OK|MB_ICONEXCLAMATION);\r
6631       return NULL;\r
6632     }\r
6633   } else {\r
6634     int err = CommDlgExtendedError();\r
6635     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6636     return FALSE;\r
6637   }\r
6638   return f;\r
6639 }\r
6640 \r
6641 \r
6642 \r
6643 VOID APIENTRY\r
6644 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6645 {\r
6646   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6647 \r
6648   /*\r
6649    * Get the first pop-up menu in the menu template. This is the\r
6650    * menu that TrackPopupMenu displays.\r
6651    */\r
6652   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6653 \r
6654   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6655 \r
6656   /*\r
6657    * TrackPopup uses screen coordinates, so convert the\r
6658    * coordinates of the mouse click to screen coordinates.\r
6659    */\r
6660   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6661 \r
6662   /* Draw and track the floating pop-up menu. */\r
6663   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6664                  pt.x, pt.y, 0, hwnd, NULL);\r
6665 \r
6666   /* Destroy the menu.*/\r
6667   DestroyMenu(hmenu);\r
6668 }\r
6669    \r
6670 typedef struct {\r
6671   HWND hDlg, hText;\r
6672   int sizeX, sizeY, newSizeX, newSizeY;\r
6673   HDWP hdwp;\r
6674 } ResizeEditPlusButtonsClosure;\r
6675 \r
6676 BOOL CALLBACK\r
6677 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6678 {\r
6679   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6680   RECT rect;\r
6681   POINT pt;\r
6682 \r
6683   if (hChild == cl->hText) return TRUE;\r
6684   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6685   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6686   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6687   ScreenToClient(cl->hDlg, &pt);\r
6688   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6689     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6690   return TRUE;\r
6691 }\r
6692 \r
6693 /* Resize a dialog that has a (rich) edit field filling most of\r
6694    the top, with a row of buttons below */\r
6695 VOID\r
6696 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6697 {\r
6698   RECT rectText;\r
6699   int newTextHeight, newTextWidth;\r
6700   ResizeEditPlusButtonsClosure cl;\r
6701   \r
6702   /*if (IsIconic(hDlg)) return;*/\r
6703   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6704   \r
6705   cl.hdwp = BeginDeferWindowPos(8);\r
6706 \r
6707   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6708   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6709   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6710   if (newTextHeight < 0) {\r
6711     newSizeY += -newTextHeight;\r
6712     newTextHeight = 0;\r
6713   }\r
6714   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6715     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6716 \r
6717   cl.hDlg = hDlg;\r
6718   cl.hText = hText;\r
6719   cl.sizeX = sizeX;\r
6720   cl.sizeY = sizeY;\r
6721   cl.newSizeX = newSizeX;\r
6722   cl.newSizeY = newSizeY;\r
6723   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6724 \r
6725   EndDeferWindowPos(cl.hdwp);\r
6726 }\r
6727 \r
6728 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6729 {\r
6730     RECT    rChild, rParent;\r
6731     int     wChild, hChild, wParent, hParent;\r
6732     int     wScreen, hScreen, xNew, yNew;\r
6733     HDC     hdc;\r
6734 \r
6735     /* Get the Height and Width of the child window */\r
6736     GetWindowRect (hwndChild, &rChild);\r
6737     wChild = rChild.right - rChild.left;\r
6738     hChild = rChild.bottom - rChild.top;\r
6739 \r
6740     /* Get the Height and Width of the parent window */\r
6741     GetWindowRect (hwndParent, &rParent);\r
6742     wParent = rParent.right - rParent.left;\r
6743     hParent = rParent.bottom - rParent.top;\r
6744 \r
6745     /* Get the display limits */\r
6746     hdc = GetDC (hwndChild);\r
6747     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6748     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6749     ReleaseDC(hwndChild, hdc);\r
6750 \r
6751     /* Calculate new X position, then adjust for screen */\r
6752     xNew = rParent.left + ((wParent - wChild) /2);\r
6753     if (xNew < 0) {\r
6754         xNew = 0;\r
6755     } else if ((xNew+wChild) > wScreen) {\r
6756         xNew = wScreen - wChild;\r
6757     }\r
6758 \r
6759     /* Calculate new Y position, then adjust for screen */\r
6760     if( mode == 0 ) {\r
6761         yNew = rParent.top  + ((hParent - hChild) /2);\r
6762     }\r
6763     else {\r
6764         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6765     }\r
6766 \r
6767     if (yNew < 0) {\r
6768         yNew = 0;\r
6769     } else if ((yNew+hChild) > hScreen) {\r
6770         yNew = hScreen - hChild;\r
6771     }\r
6772 \r
6773     /* Set it, and return */\r
6774     return SetWindowPos (hwndChild, NULL,\r
6775                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6776 }\r
6777 \r
6778 /* Center one window over another */\r
6779 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6780 {\r
6781     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6782 }\r
6783 \r
6784 /*---------------------------------------------------------------------------*\\r
6785  *\r
6786  * Startup Dialog functions\r
6787  *\r
6788 \*---------------------------------------------------------------------------*/\r
6789 void\r
6790 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6791 {\r
6792   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6793 \r
6794   while (*cd != NULL) {\r
6795     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6796     cd++;\r
6797   }\r
6798 }\r
6799 \r
6800 void\r
6801 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6802 {\r
6803   char buf1[ARG_MAX];\r
6804   int len;\r
6805 \r
6806   if (str[0] == '@') {\r
6807     FILE* f = fopen(str + 1, "r");\r
6808     if (f == NULL) {\r
6809       DisplayFatalError(str + 1, errno, 2);\r
6810       return;\r
6811     }\r
6812     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6813     fclose(f);\r
6814     buf1[len] = NULLCHAR;\r
6815     str = buf1;\r
6816   }\r
6817 \r
6818   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6819 \r
6820   for (;;) {\r
6821     char buf[MSG_SIZ];\r
6822     char *end = strchr(str, '\n');\r
6823     if (end == NULL) return;\r
6824     memcpy(buf, str, end - str);\r
6825     buf[end - str] = NULLCHAR;\r
6826     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6827     str = end + 1;\r
6828   }\r
6829 }\r
6830 \r
6831 void\r
6832 SetStartupDialogEnables(HWND hDlg)\r
6833 {\r
6834   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6835     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6836     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6837   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6838     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6839   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6840     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6841   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6842     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6843   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6844     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6845     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6846     IsDlgButtonChecked(hDlg, OPT_View));\r
6847 }\r
6848 \r
6849 char *\r
6850 QuoteForFilename(char *filename)\r
6851 {\r
6852   int dquote, space;\r
6853   dquote = strchr(filename, '"') != NULL;\r
6854   space = strchr(filename, ' ') != NULL;\r
6855   if (dquote || space) {\r
6856     if (dquote) {\r
6857       return "'";\r
6858     } else {\r
6859       return "\"";\r
6860     }\r
6861   } else {\r
6862     return "";\r
6863   }\r
6864 }\r
6865 \r
6866 VOID\r
6867 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6868 {\r
6869   char buf[MSG_SIZ];\r
6870   char *q;\r
6871 \r
6872   InitComboStringsFromOption(hwndCombo, nthnames);\r
6873   q = QuoteForFilename(nthcp);\r
6874   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6875   if (*nthdir != NULLCHAR) {\r
6876     q = QuoteForFilename(nthdir);\r
6877     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6878   }\r
6879   if (*nthcp == NULLCHAR) {\r
6880     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6881   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6882     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6883     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6884   }\r
6885 }\r
6886 \r
6887 LRESULT CALLBACK\r
6888 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6889 {\r
6890   char buf[MSG_SIZ];\r
6891   HANDLE hwndCombo;\r
6892   char *p;\r
6893 \r
6894   switch (message) {\r
6895   case WM_INITDIALOG:\r
6896     /* Center the dialog */\r
6897     CenterWindow (hDlg, GetDesktopWindow());\r
6898     /* Initialize the dialog items */\r
6899     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6900                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6901                   firstChessProgramNames);\r
6902     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6903                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6904                   secondChessProgramNames);\r
6905     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6906     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6907     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6908     if (*appData.icsHelper != NULLCHAR) {\r
6909       char *q = QuoteForFilename(appData.icsHelper);\r
6910       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6911     }\r
6912     if (*appData.icsHost == NULLCHAR) {\r
6913       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6914       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6915     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6916       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6917       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6918     }\r
6919 \r
6920     if (appData.icsActive) {\r
6921       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6922     }\r
6923     else if (appData.noChessProgram) {\r
6924       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6925     }\r
6926     else {\r
6927       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6928     }\r
6929 \r
6930     SetStartupDialogEnables(hDlg);\r
6931     return TRUE;\r
6932 \r
6933   case WM_COMMAND:\r
6934     switch (LOWORD(wParam)) {\r
6935     case IDOK:\r
6936       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6937         strcpy(buf, "/fcp=");\r
6938         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6939         p = buf;\r
6940         ParseArgs(StringGet, &p);\r
6941         strcpy(buf, "/scp=");\r
6942         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6943         p = buf;\r
6944         ParseArgs(StringGet, &p);\r
6945         appData.noChessProgram = FALSE;\r
6946         appData.icsActive = FALSE;\r
6947       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6948         strcpy(buf, "/ics /icshost=");\r
6949         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6950         p = buf;\r
6951         ParseArgs(StringGet, &p);\r
6952         if (appData.zippyPlay) {\r
6953           strcpy(buf, "/fcp=");\r
6954           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6955           p = buf;\r
6956           ParseArgs(StringGet, &p);\r
6957         }\r
6958       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6959         appData.noChessProgram = TRUE;\r
6960         appData.icsActive = FALSE;\r
6961       } else {\r
6962         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6963                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6964         return TRUE;\r
6965       }\r
6966       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6967         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6968         p = buf;\r
6969         ParseArgs(StringGet, &p);\r
6970       }\r
6971       EndDialog(hDlg, TRUE);\r
6972       return TRUE;\r
6973 \r
6974     case IDCANCEL:\r
6975       ExitEvent(0);\r
6976       return TRUE;\r
6977 \r
6978     case IDM_HELPCONTENTS:\r
6979       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6980         MessageBox (GetFocus(),\r
6981                     "Unable to activate help",\r
6982                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6983       }\r
6984       break;\r
6985 \r
6986     default:\r
6987       SetStartupDialogEnables(hDlg);\r
6988       break;\r
6989     }\r
6990     break;\r
6991   }\r
6992   return FALSE;\r
6993 }\r
6994 \r
6995 /*---------------------------------------------------------------------------*\\r
6996  *\r
6997  * About box dialog functions\r
6998  *\r
6999 \*---------------------------------------------------------------------------*/\r
7000 \r
7001 /* Process messages for "About" dialog box */\r
7002 LRESULT CALLBACK\r
7003 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7004 {\r
7005   switch (message) {\r
7006   case WM_INITDIALOG: /* message: initialize dialog box */\r
7007     /* Center the dialog over the application window */\r
7008     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7009     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7010     return (TRUE);\r
7011 \r
7012   case WM_COMMAND: /* message: received a command */\r
7013     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7014         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7015       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7016       return (TRUE);\r
7017     }\r
7018     break;\r
7019   }\r
7020   return (FALSE);\r
7021 }\r
7022 \r
7023 /*---------------------------------------------------------------------------*\\r
7024  *\r
7025  * Comment Dialog functions\r
7026  *\r
7027 \*---------------------------------------------------------------------------*/\r
7028 \r
7029 LRESULT CALLBACK\r
7030 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7031 {\r
7032   static HANDLE hwndText = NULL;\r
7033   int len, newSizeX, newSizeY, flags;\r
7034   static int sizeX, sizeY;\r
7035   char *str;\r
7036   RECT rect;\r
7037   MINMAXINFO *mmi;\r
7038 \r
7039   switch (message) {\r
7040   case WM_INITDIALOG: /* message: initialize dialog box */\r
7041     /* Initialize the dialog items */\r
7042     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7043     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7044     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7045     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7046     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7047     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7048     SetWindowText(hDlg, commentTitle);\r
7049     if (editComment) {\r
7050       SetFocus(hwndText);\r
7051     } else {\r
7052       SetFocus(GetDlgItem(hDlg, IDOK));\r
7053     }\r
7054     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7055                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7056                 MAKELPARAM(FALSE, 0));\r
7057     /* Size and position the dialog */\r
7058     if (!commentDialog) {\r
7059       commentDialog = hDlg;\r
7060       flags = SWP_NOZORDER;\r
7061       GetClientRect(hDlg, &rect);\r
7062       sizeX = rect.right;\r
7063       sizeY = rect.bottom;\r
7064       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7065           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7066         WINDOWPLACEMENT wp;\r
7067         EnsureOnScreen(&commentX, &commentY);\r
7068         wp.length = sizeof(WINDOWPLACEMENT);\r
7069         wp.flags = 0;\r
7070         wp.showCmd = SW_SHOW;\r
7071         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7072         wp.rcNormalPosition.left = commentX;\r
7073         wp.rcNormalPosition.right = commentX + commentW;\r
7074         wp.rcNormalPosition.top = commentY;\r
7075         wp.rcNormalPosition.bottom = commentY + commentH;\r
7076         SetWindowPlacement(hDlg, &wp);\r
7077 \r
7078         GetClientRect(hDlg, &rect);\r
7079         newSizeX = rect.right;\r
7080         newSizeY = rect.bottom;\r
7081         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7082                               newSizeX, newSizeY);\r
7083         sizeX = newSizeX;\r
7084         sizeY = newSizeY;\r
7085       }\r
7086     }\r
7087     return FALSE;\r
7088 \r
7089   case WM_COMMAND: /* message: received a command */\r
7090     switch (LOWORD(wParam)) {\r
7091     case IDOK:\r
7092       if (editComment) {\r
7093         char *p, *q;\r
7094         /* Read changed options from the dialog box */\r
7095         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7096         len = GetWindowTextLength(hwndText);\r
7097         str = (char *) malloc(len + 1);\r
7098         GetWindowText(hwndText, str, len + 1);\r
7099         p = q = str;\r
7100         while (*q) {\r
7101           if (*q == '\r')\r
7102             q++;\r
7103           else\r
7104             *p++ = *q++;\r
7105         }\r
7106         *p = NULLCHAR;\r
7107         ReplaceComment(commentIndex, str);\r
7108         free(str);\r
7109       }\r
7110       CommentPopDown();\r
7111       return TRUE;\r
7112 \r
7113     case IDCANCEL:\r
7114     case OPT_CancelComment:\r
7115       CommentPopDown();\r
7116       return TRUE;\r
7117 \r
7118     case OPT_ClearComment:\r
7119       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7120       break;\r
7121 \r
7122     case OPT_EditComment:\r
7123       EditCommentEvent();\r
7124       return TRUE;\r
7125 \r
7126     default:\r
7127       break;\r
7128     }\r
7129     break;\r
7130 \r
7131   case WM_SIZE:\r
7132     newSizeX = LOWORD(lParam);\r
7133     newSizeY = HIWORD(lParam);\r
7134     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7135     sizeX = newSizeX;\r
7136     sizeY = newSizeY;\r
7137     break;\r
7138 \r
7139   case WM_GETMINMAXINFO:\r
7140     /* Prevent resizing window too small */\r
7141     mmi = (MINMAXINFO *) lParam;\r
7142     mmi->ptMinTrackSize.x = 100;\r
7143     mmi->ptMinTrackSize.y = 100;\r
7144     break;\r
7145   }\r
7146   return FALSE;\r
7147 }\r
7148 \r
7149 VOID\r
7150 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7151 {\r
7152   FARPROC lpProc;\r
7153   char *p, *q;\r
7154 \r
7155   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7156 \r
7157   if (str == NULL) str = "";\r
7158   p = (char *) malloc(2 * strlen(str) + 2);\r
7159   q = p;\r
7160   while (*str) {\r
7161     if (*str == '\n') *q++ = '\r';\r
7162     *q++ = *str++;\r
7163   }\r
7164   *q = NULLCHAR;\r
7165   if (commentText != NULL) free(commentText);\r
7166 \r
7167   commentIndex = index;\r
7168   commentTitle = title;\r
7169   commentText = p;\r
7170   editComment = edit;\r
7171 \r
7172   if (commentDialog) {\r
7173     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7174     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7175   } else {\r
7176     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7177     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7178                  hwndMain, (DLGPROC)lpProc);\r
7179     FreeProcInstance(lpProc);\r
7180   }\r
7181   commentDialogUp = TRUE;\r
7182 }\r
7183 \r
7184 \r
7185 /*---------------------------------------------------------------------------*\\r
7186  *\r
7187  * Type-in move dialog functions\r
7188  * \r
7189 \*---------------------------------------------------------------------------*/\r
7190 \r
7191 LRESULT CALLBACK\r
7192 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7193 {\r
7194   char move[MSG_SIZ];\r
7195   HWND hInput;\r
7196   ChessMove moveType;\r
7197   int fromX, fromY, toX, toY;\r
7198   char promoChar;\r
7199 \r
7200   switch (message) {\r
7201   case WM_INITDIALOG:\r
7202     move[0] = (char) lParam;\r
7203     move[1] = NULLCHAR;\r
7204     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7205     hInput = GetDlgItem(hDlg, OPT_Move);\r
7206     SetWindowText(hInput, move);\r
7207     SetFocus(hInput);\r
7208     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7209     return FALSE;\r
7210 \r
7211   case WM_COMMAND:\r
7212     switch (LOWORD(wParam)) {\r
7213     case IDOK:\r
7214       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7215         gameMode != Training) {\r
7216         DisplayMoveError("Displayed move is not current");\r
7217       } else {\r
7218         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7219         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7220           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7221           if (gameMode != Training)\r
7222               forwardMostMove = currentMove;\r
7223           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7224         } else {\r
7225           DisplayMoveError("Could not parse move");\r
7226         }\r
7227       }\r
7228       EndDialog(hDlg, TRUE);\r
7229       return TRUE;\r
7230     case IDCANCEL:\r
7231       EndDialog(hDlg, FALSE);\r
7232       return TRUE;\r
7233     default:\r
7234       break;\r
7235     }\r
7236     break;\r
7237   }\r
7238   return FALSE;\r
7239 }\r
7240 \r
7241 VOID\r
7242 PopUpMoveDialog(char firstchar)\r
7243 {\r
7244     FARPROC lpProc;\r
7245     \r
7246     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7247         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7248         gameMode == AnalyzeMode || gameMode == EditGame || \r
7249         gameMode == EditPosition || gameMode == IcsExamining ||\r
7250         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7251         gameMode == Training) {\r
7252       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7253       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7254         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7255       FreeProcInstance(lpProc);\r
7256     }\r
7257 }\r
7258 \r
7259 /*---------------------------------------------------------------------------*\\r
7260  *\r
7261  * Type-in name dialog functions\r
7262  * \r
7263 \*---------------------------------------------------------------------------*/\r
7264 \r
7265 LRESULT CALLBACK\r
7266 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7267 {\r
7268   char move[MSG_SIZ];\r
7269   HWND hInput;\r
7270 \r
7271   switch (message) {\r
7272   case WM_INITDIALOG:\r
7273     move[0] = (char) lParam;\r
7274     move[1] = NULLCHAR;\r
7275     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7276     hInput = GetDlgItem(hDlg, OPT_Name);\r
7277     SetWindowText(hInput, move);\r
7278     SetFocus(hInput);\r
7279     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7280     return FALSE;\r
7281 \r
7282   case WM_COMMAND:\r
7283     switch (LOWORD(wParam)) {\r
7284     case IDOK:\r
7285       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7286       appData.userName = strdup(move);\r
7287 \r
7288       EndDialog(hDlg, TRUE);\r
7289       return TRUE;\r
7290     case IDCANCEL:\r
7291       EndDialog(hDlg, FALSE);\r
7292       return TRUE;\r
7293     default:\r
7294       break;\r
7295     }\r
7296     break;\r
7297   }\r
7298   return FALSE;\r
7299 }\r
7300 \r
7301 VOID\r
7302 PopUpNameDialog(char firstchar)\r
7303 {\r
7304     FARPROC lpProc;\r
7305     \r
7306       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7307       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7308         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7309       FreeProcInstance(lpProc);\r
7310 }\r
7311 \r
7312 /*---------------------------------------------------------------------------*\\r
7313  *\r
7314  *  Error dialogs\r
7315  * \r
7316 \*---------------------------------------------------------------------------*/\r
7317 \r
7318 /* Nonmodal error box */\r
7319 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7320                              WPARAM wParam, LPARAM lParam);\r
7321 \r
7322 VOID\r
7323 ErrorPopUp(char *title, char *content)\r
7324 {\r
7325   FARPROC lpProc;\r
7326   char *p, *q;\r
7327   BOOLEAN modal = hwndMain == NULL;\r
7328 \r
7329   p = content;\r
7330   q = errorMessage;\r
7331   while (*p) {\r
7332     if (*p == '\n') {\r
7333       if (modal) {\r
7334         *q++ = ' ';\r
7335         p++;\r
7336       } else {\r
7337         *q++ = '\r';\r
7338         *q++ = *p++;\r
7339       }\r
7340     } else {\r
7341       *q++ = *p++;\r
7342     }\r
7343   }\r
7344   *q = NULLCHAR;\r
7345   strncpy(errorTitle, title, sizeof(errorTitle));\r
7346   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7347   \r
7348   if (modal) {\r
7349     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7350   } else {\r
7351     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7352     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7353                  hwndMain, (DLGPROC)lpProc);\r
7354     FreeProcInstance(lpProc);\r
7355   }\r
7356 }\r
7357 \r
7358 VOID\r
7359 ErrorPopDown()\r
7360 {\r
7361   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7362   if (errorDialog == NULL) return;\r
7363   DestroyWindow(errorDialog);\r
7364   errorDialog = NULL;\r
7365 }\r
7366 \r
7367 LRESULT CALLBACK\r
7368 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7369 {\r
7370   HANDLE hwndText;\r
7371   RECT rChild;\r
7372 \r
7373   switch (message) {\r
7374   case WM_INITDIALOG:\r
7375     GetWindowRect(hDlg, &rChild);\r
7376 \r
7377     /*\r
7378     SetWindowPos(hDlg, NULL, rChild.left,\r
7379       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7380       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7381     */\r
7382 \r
7383     /* \r
7384         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7385         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7386         and it doesn't work when you resize the dialog.\r
7387         For now, just give it a default position.\r
7388     */\r
7389     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7390 \r
7391     errorDialog = hDlg;\r
7392     SetWindowText(hDlg, errorTitle);\r
7393     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7394     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7395     return FALSE;\r
7396 \r
7397   case WM_COMMAND:\r
7398     switch (LOWORD(wParam)) {\r
7399     case IDOK:\r
7400     case IDCANCEL:\r
7401       if (errorDialog == hDlg) errorDialog = NULL;\r
7402       DestroyWindow(hDlg);\r
7403       return TRUE;\r
7404 \r
7405     default:\r
7406       break;\r
7407     }\r
7408     break;\r
7409   }\r
7410   return FALSE;\r
7411 }\r
7412 \r
7413 #ifdef GOTHIC\r
7414 HWND gothicDialog = NULL;\r
7415 \r
7416 LRESULT CALLBACK\r
7417 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7418 {\r
7419   HANDLE hwndText;\r
7420   RECT rChild;\r
7421   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7422 \r
7423   switch (message) {\r
7424   case WM_INITDIALOG:\r
7425     GetWindowRect(hDlg, &rChild);\r
7426 \r
7427     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7428                                                              SWP_NOZORDER);\r
7429 \r
7430     /* \r
7431         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7432         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7433         and it doesn't work when you resize the dialog.\r
7434         For now, just give it a default position.\r
7435     */\r
7436     gothicDialog = hDlg;\r
7437     SetWindowText(hDlg, errorTitle);\r
7438     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7439     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7440     return FALSE;\r
7441 \r
7442   case WM_COMMAND:\r
7443     switch (LOWORD(wParam)) {\r
7444     case IDOK:\r
7445     case IDCANCEL:\r
7446       if (errorDialog == hDlg) errorDialog = NULL;\r
7447       DestroyWindow(hDlg);\r
7448       return TRUE;\r
7449 \r
7450     default:\r
7451       break;\r
7452     }\r
7453     break;\r
7454   }\r
7455   return FALSE;\r
7456 }\r
7457 \r
7458 VOID\r
7459 GothicPopUp(char *title, VariantClass variant)\r
7460 {\r
7461   FARPROC lpProc;\r
7462   static char *lastTitle;\r
7463 \r
7464   strncpy(errorTitle, title, sizeof(errorTitle));\r
7465   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7466 \r
7467   if(lastTitle != title && gothicDialog != NULL) {\r
7468     DestroyWindow(gothicDialog);\r
7469     gothicDialog = NULL;\r
7470   }\r
7471   if(variant != VariantNormal && gothicDialog == NULL) {\r
7472     title = lastTitle;\r
7473     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7474     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7475                  hwndMain, (DLGPROC)lpProc);\r
7476     FreeProcInstance(lpProc);\r
7477   }\r
7478 }\r
7479 #endif\r
7480 \r
7481 /*---------------------------------------------------------------------------*\\r
7482  *\r
7483  *  Ics Interaction console functions\r
7484  *\r
7485 \*---------------------------------------------------------------------------*/\r
7486 \r
7487 #define HISTORY_SIZE 64\r
7488 static char *history[HISTORY_SIZE];\r
7489 int histIn = 0, histP = 0;\r
7490 \r
7491 VOID\r
7492 SaveInHistory(char *cmd)\r
7493 {\r
7494   if (history[histIn] != NULL) {\r
7495     free(history[histIn]);\r
7496     history[histIn] = NULL;\r
7497   }\r
7498   if (*cmd == NULLCHAR) return;\r
7499   history[histIn] = StrSave(cmd);\r
7500   histIn = (histIn + 1) % HISTORY_SIZE;\r
7501   if (history[histIn] != NULL) {\r
7502     free(history[histIn]);\r
7503     history[histIn] = NULL;\r
7504   }\r
7505   histP = histIn;\r
7506 }\r
7507 \r
7508 char *\r
7509 PrevInHistory(char *cmd)\r
7510 {\r
7511   int newhp;\r
7512   if (histP == histIn) {\r
7513     if (history[histIn] != NULL) free(history[histIn]);\r
7514     history[histIn] = StrSave(cmd);\r
7515   }\r
7516   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7517   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7518   histP = newhp;\r
7519   return history[histP];\r
7520 }\r
7521 \r
7522 char *\r
7523 NextInHistory()\r
7524 {\r
7525   if (histP == histIn) return NULL;\r
7526   histP = (histP + 1) % HISTORY_SIZE;\r
7527   return history[histP];\r
7528 }\r
7529 \r
7530 typedef struct {\r
7531   char *item;\r
7532   char *command;\r
7533   BOOLEAN getname;\r
7534   BOOLEAN immediate;\r
7535 } IcsTextMenuEntry;\r
7536 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7537 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7538 \r
7539 void\r
7540 ParseIcsTextMenu(char *icsTextMenuString)\r
7541 {\r
7542 //  int flags = 0;\r
7543   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7544   char *p = icsTextMenuString;\r
7545   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7546     free(e->item);\r
7547     e->item = NULL;\r
7548     if (e->command != NULL) {\r
7549       free(e->command);\r
7550       e->command = NULL;\r
7551     }\r
7552     e++;\r
7553   }\r
7554   e = icsTextMenuEntry;\r
7555   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7556     if (*p == ';' || *p == '\n') {\r
7557       e->item = strdup("-");\r
7558       e->command = NULL;\r
7559       p++;\r
7560     } else if (*p == '-') {\r
7561       e->item = strdup("-");\r
7562       e->command = NULL;\r
7563       p++;\r
7564       if (*p) p++;\r
7565     } else {\r
7566       char *q, *r, *s, *t;\r
7567       char c;\r
7568       q = strchr(p, ',');\r
7569       if (q == NULL) break;\r
7570       *q = NULLCHAR;\r
7571       r = strchr(q + 1, ',');\r
7572       if (r == NULL) break;\r
7573       *r = NULLCHAR;\r
7574       s = strchr(r + 1, ',');\r
7575       if (s == NULL) break;\r
7576       *s = NULLCHAR;\r
7577       c = ';';\r
7578       t = strchr(s + 1, c);\r
7579       if (t == NULL) {\r
7580         c = '\n';\r
7581         t = strchr(s + 1, c);\r
7582       }\r
7583       if (t != NULL) *t = NULLCHAR;\r
7584       e->item = strdup(p);\r
7585       e->command = strdup(q + 1);\r
7586       e->getname = *(r + 1) != '0';\r
7587       e->immediate = *(s + 1) != '0';\r
7588       *q = ',';\r
7589       *r = ',';\r
7590       *s = ',';\r
7591       if (t == NULL) break;\r
7592       *t = c;\r
7593       p = t + 1;\r
7594     }\r
7595     e++;\r
7596   } \r
7597 }\r
7598 \r
7599 HMENU\r
7600 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7601 {\r
7602   HMENU hmenu, h;\r
7603   int i = 0;\r
7604   hmenu = LoadMenu(hInst, "TextMenu");\r
7605   h = GetSubMenu(hmenu, 0);\r
7606   while (e->item) {\r
7607     if (strcmp(e->item, "-") == 0) {\r
7608       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7609     } else {\r
7610       if (e->item[0] == '|') {\r
7611         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7612                    IDM_CommandX + i, &e->item[1]);\r
7613       } else {\r
7614         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7615       }\r
7616     }\r
7617     e++;\r
7618     i++;\r
7619   } \r
7620   return hmenu;\r
7621 }\r
7622 \r
7623 WNDPROC consoleTextWindowProc;\r
7624 \r
7625 void\r
7626 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7627 {\r
7628   char buf[MSG_SIZ], name[MSG_SIZ];\r
7629   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7630   CHARRANGE sel;\r
7631 \r
7632   if (!getname) {\r
7633     SetWindowText(hInput, command);\r
7634     if (immediate) {\r
7635       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7636     } else {\r
7637       sel.cpMin = 999999;\r
7638       sel.cpMax = 999999;\r
7639       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7640       SetFocus(hInput);\r
7641     }\r
7642     return;\r
7643   }    \r
7644   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7645   if (sel.cpMin == sel.cpMax) {\r
7646     /* Expand to surrounding word */\r
7647     TEXTRANGE tr;\r
7648     do {\r
7649       tr.chrg.cpMax = sel.cpMin;\r
7650       tr.chrg.cpMin = --sel.cpMin;\r
7651       if (sel.cpMin < 0) break;\r
7652       tr.lpstrText = name;\r
7653       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7654     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7655     sel.cpMin++;\r
7656 \r
7657     do {\r
7658       tr.chrg.cpMin = sel.cpMax;\r
7659       tr.chrg.cpMax = ++sel.cpMax;\r
7660       tr.lpstrText = name;\r
7661       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7662     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7663     sel.cpMax--;\r
7664 \r
7665     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7666       MessageBeep(MB_ICONEXCLAMATION);\r
7667       return;\r
7668     }\r
7669     tr.chrg = sel;\r
7670     tr.lpstrText = name;\r
7671     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7672   } else {\r
7673     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7674       MessageBeep(MB_ICONEXCLAMATION);\r
7675       return;\r
7676     }\r
7677     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7678   }\r
7679   if (immediate) {\r
7680     sprintf(buf, "%s %s", command, name);\r
7681     SetWindowText(hInput, buf);\r
7682     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7683   } else {\r
7684     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7685     SetWindowText(hInput, buf);\r
7686     sel.cpMin = 999999;\r
7687     sel.cpMax = 999999;\r
7688     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7689     SetFocus(hInput);\r
7690   }\r
7691 }\r
7692 \r
7693 LRESULT CALLBACK \r
7694 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7695 {\r
7696   HWND hInput;\r
7697   CHARRANGE sel;\r
7698 \r
7699   switch (message) {\r
7700   case WM_KEYDOWN:\r
7701     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7702     switch (wParam) {\r
7703     case VK_PRIOR:\r
7704       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7705       return 0;\r
7706     case VK_NEXT:\r
7707       sel.cpMin = 999999;\r
7708       sel.cpMax = 999999;\r
7709       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7710       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7711       return 0;\r
7712     }\r
7713     break;\r
7714   case WM_CHAR:\r
7715     if (wParam == '\t') {\r
7716       if (GetKeyState(VK_SHIFT) < 0) {\r
7717         /* shifted */\r
7718         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7719         if (buttonDesc[0].hwnd) {\r
7720           SetFocus(buttonDesc[0].hwnd);\r
7721         } else {\r
7722           SetFocus(hwndMain);\r
7723         }\r
7724       } else {\r
7725         /* unshifted */\r
7726         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7727       }\r
7728     } else {\r
7729       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7730       SetFocus(hInput);\r
7731       SendMessage(hInput, message, wParam, lParam);\r
7732     }\r
7733     return 0;\r
7734   case WM_PASTE:\r
7735     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7736     SetFocus(hInput);\r
7737     return SendMessage(hInput, message, wParam, lParam);\r
7738   case WM_MBUTTONDOWN:\r
7739     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7740   case WM_RBUTTONDOWN:\r
7741     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7742       /* Move selection here if it was empty */\r
7743       POINT pt;\r
7744       pt.x = LOWORD(lParam);\r
7745       pt.y = HIWORD(lParam);\r
7746       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7747       if (sel.cpMin == sel.cpMax) {\r
7748         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7749         sel.cpMax = sel.cpMin;\r
7750         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7751       }\r
7752       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7753     }\r
7754     return 0;\r
7755   case WM_RBUTTONUP:\r
7756     if (GetKeyState(VK_SHIFT) & ~1) {\r
7757       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7758         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7759     } else {\r
7760       POINT pt;\r
7761       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7762       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7763       if (sel.cpMin == sel.cpMax) {\r
7764         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7765         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7766       }\r
7767       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7768         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7769       }\r
7770       pt.x = LOWORD(lParam);\r
7771       pt.y = HIWORD(lParam);\r
7772       MenuPopup(hwnd, pt, hmenu, -1);\r
7773     }\r
7774     return 0;\r
7775   case WM_COMMAND:\r
7776     switch (LOWORD(wParam)) {\r
7777     case IDM_QuickPaste:\r
7778       {\r
7779         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7780         if (sel.cpMin == sel.cpMax) {\r
7781           MessageBeep(MB_ICONEXCLAMATION);\r
7782           return 0;\r
7783         }\r
7784         SendMessage(hwnd, WM_COPY, 0, 0);\r
7785         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7786         SendMessage(hInput, WM_PASTE, 0, 0);\r
7787         SetFocus(hInput);\r
7788         return 0;\r
7789       }\r
7790     case IDM_Cut:\r
7791       SendMessage(hwnd, WM_CUT, 0, 0);\r
7792       return 0;\r
7793     case IDM_Paste:\r
7794       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7795       return 0;\r
7796     case IDM_Copy:\r
7797       SendMessage(hwnd, WM_COPY, 0, 0);\r
7798       return 0;\r
7799     default:\r
7800       {\r
7801         int i = LOWORD(wParam) - IDM_CommandX;\r
7802         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7803             icsTextMenuEntry[i].command != NULL) {\r
7804           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7805                    icsTextMenuEntry[i].getname,\r
7806                    icsTextMenuEntry[i].immediate);\r
7807           return 0;\r
7808         }\r
7809       }\r
7810       break;\r
7811     }\r
7812     break;\r
7813   }\r
7814   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7815 }\r
7816 \r
7817 WNDPROC consoleInputWindowProc;\r
7818 \r
7819 LRESULT CALLBACK\r
7820 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7821 {\r
7822   char buf[MSG_SIZ];\r
7823   char *p;\r
7824   static BOOL sendNextChar = FALSE;\r
7825   static BOOL quoteNextChar = FALSE;\r
7826   InputSource *is = consoleInputSource;\r
7827   CHARFORMAT cf;\r
7828   CHARRANGE sel;\r
7829 \r
7830   switch (message) {\r
7831   case WM_CHAR:\r
7832     if (!appData.localLineEditing || sendNextChar) {\r
7833       is->buf[0] = (CHAR) wParam;\r
7834       is->count = 1;\r
7835       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7836       sendNextChar = FALSE;\r
7837       return 0;\r
7838     }\r
7839     if (quoteNextChar) {\r
7840       buf[0] = (char) wParam;\r
7841       buf[1] = NULLCHAR;\r
7842       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7843       quoteNextChar = FALSE;\r
7844       return 0;\r
7845     }\r
7846     switch (wParam) {\r
7847     case '\r':   /* Enter key */\r
7848       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7849       if (consoleEcho) SaveInHistory(is->buf);\r
7850       is->buf[is->count++] = '\n';\r
7851       is->buf[is->count] = NULLCHAR;\r
7852       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7853       if (consoleEcho) {\r
7854         ConsoleOutput(is->buf, is->count, TRUE);\r
7855       } else if (appData.localLineEditing) {\r
7856         ConsoleOutput("\n", 1, TRUE);\r
7857       }\r
7858       /* fall thru */\r
7859     case '\033': /* Escape key */\r
7860       SetWindowText(hwnd, "");\r
7861       cf.cbSize = sizeof(CHARFORMAT);\r
7862       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7863       if (consoleEcho) {\r
7864         cf.crTextColor = textAttribs[ColorNormal].color;\r
7865       } else {\r
7866         cf.crTextColor = COLOR_ECHOOFF;\r
7867       }\r
7868       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7869       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7870       return 0;\r
7871     case '\t':   /* Tab key */\r
7872       if (GetKeyState(VK_SHIFT) < 0) {\r
7873         /* shifted */\r
7874         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7875       } else {\r
7876         /* unshifted */\r
7877         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7878         if (buttonDesc[0].hwnd) {\r
7879           SetFocus(buttonDesc[0].hwnd);\r
7880         } else {\r
7881           SetFocus(hwndMain);\r
7882         }\r
7883       }\r
7884       return 0;\r
7885     case '\023': /* Ctrl+S */\r
7886       sendNextChar = TRUE;\r
7887       return 0;\r
7888     case '\021': /* Ctrl+Q */\r
7889       quoteNextChar = TRUE;\r
7890       return 0;\r
7891     default:\r
7892       break;\r
7893     }\r
7894     break;\r
7895   case WM_KEYDOWN:\r
7896     switch (wParam) {\r
7897     case VK_UP:\r
7898       GetWindowText(hwnd, buf, MSG_SIZ);\r
7899       p = PrevInHistory(buf);\r
7900       if (p != NULL) {\r
7901         SetWindowText(hwnd, p);\r
7902         sel.cpMin = 999999;\r
7903         sel.cpMax = 999999;\r
7904         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7905         return 0;\r
7906       }\r
7907       break;\r
7908     case VK_DOWN:\r
7909       p = NextInHistory();\r
7910       if (p != NULL) {\r
7911         SetWindowText(hwnd, p);\r
7912         sel.cpMin = 999999;\r
7913         sel.cpMax = 999999;\r
7914         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7915         return 0;\r
7916       }\r
7917       break;\r
7918     case VK_HOME:\r
7919     case VK_END:\r
7920       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7921       /* fall thru */\r
7922     case VK_PRIOR:\r
7923     case VK_NEXT:\r
7924       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7925       return 0;\r
7926     }\r
7927     break;\r
7928   case WM_MBUTTONDOWN:\r
7929     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7930       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7931     break;\r
7932   case WM_RBUTTONUP:\r
7933     if (GetKeyState(VK_SHIFT) & ~1) {\r
7934       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7935         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7936     } else {\r
7937       POINT pt;\r
7938       HMENU hmenu;\r
7939       hmenu = LoadMenu(hInst, "InputMenu");\r
7940       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7941       if (sel.cpMin == sel.cpMax) {\r
7942         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7943         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7944       }\r
7945       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7946         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7947       }\r
7948       pt.x = LOWORD(lParam);\r
7949       pt.y = HIWORD(lParam);\r
7950       MenuPopup(hwnd, pt, hmenu, -1);\r
7951     }\r
7952     return 0;\r
7953   case WM_COMMAND:\r
7954     switch (LOWORD(wParam)) { \r
7955     case IDM_Undo:\r
7956       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7957       return 0;\r
7958     case IDM_SelectAll:\r
7959       sel.cpMin = 0;\r
7960       sel.cpMax = -1; /*999999?*/\r
7961       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7962       return 0;\r
7963     case IDM_Cut:\r
7964       SendMessage(hwnd, WM_CUT, 0, 0);\r
7965       return 0;\r
7966     case IDM_Paste:\r
7967       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7968       return 0;\r
7969     case IDM_Copy:\r
7970       SendMessage(hwnd, WM_COPY, 0, 0);\r
7971       return 0;\r
7972     }\r
7973     break;\r
7974   }\r
7975   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7976 }\r
7977 \r
7978 #define CO_MAX  100000\r
7979 #define CO_TRIM   1000\r
7980 \r
7981 LRESULT CALLBACK\r
7982 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7983 {\r
7984   static SnapData sd;\r
7985   static HWND hText, hInput /*, hFocus*/;\r
7986 //  InputSource *is = consoleInputSource;\r
7987   RECT rect;\r
7988   static int sizeX, sizeY;\r
7989   int newSizeX, newSizeY;\r
7990   MINMAXINFO *mmi;\r
7991 \r
7992   switch (message) {\r
7993   case WM_INITDIALOG: /* message: initialize dialog box */\r
7994     hwndConsole = hDlg;\r
7995     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7996     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7997     SetFocus(hInput);\r
7998     consoleTextWindowProc = (WNDPROC)\r
7999       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8000     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8001     consoleInputWindowProc = (WNDPROC)\r
8002       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8003     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8004     Colorize(ColorNormal, TRUE);\r
8005     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8006     ChangedConsoleFont();\r
8007     GetClientRect(hDlg, &rect);\r
8008     sizeX = rect.right;\r
8009     sizeY = rect.bottom;\r
8010     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8011         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8012       WINDOWPLACEMENT wp;\r
8013       EnsureOnScreen(&consoleX, &consoleY);\r
8014       wp.length = sizeof(WINDOWPLACEMENT);\r
8015       wp.flags = 0;\r
8016       wp.showCmd = SW_SHOW;\r
8017       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8018       wp.rcNormalPosition.left = consoleX;\r
8019       wp.rcNormalPosition.right = consoleX + consoleW;\r
8020       wp.rcNormalPosition.top = consoleY;\r
8021       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8022       SetWindowPlacement(hDlg, &wp);\r
8023     }\r
8024 #if 0 \r
8025    // [HGM] Chessknight's change 2004-07-13\r
8026    else { /* Determine Defaults */\r
8027        WINDOWPLACEMENT wp;\r
8028        consoleX = winWidth + 1;\r
8029        consoleY = boardY;\r
8030        consoleW = screenWidth -  winWidth;\r
8031        consoleH = winHeight;\r
8032        EnsureOnScreen(&consoleX, &consoleY);\r
8033        wp.length = sizeof(WINDOWPLACEMENT);\r
8034        wp.flags = 0;\r
8035        wp.showCmd = SW_SHOW;\r
8036        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8037        wp.rcNormalPosition.left = consoleX;\r
8038        wp.rcNormalPosition.right = consoleX + consoleW;\r
8039        wp.rcNormalPosition.top = consoleY;\r
8040        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8041        SetWindowPlacement(hDlg, &wp);\r
8042     }\r
8043 #endif\r
8044     return FALSE;\r
8045 \r
8046   case WM_SETFOCUS:\r
8047     SetFocus(hInput);\r
8048     return 0;\r
8049 \r
8050   case WM_CLOSE:\r
8051     ExitEvent(0);\r
8052     /* not reached */\r
8053     break;\r
8054 \r
8055   case WM_SIZE:\r
8056     if (IsIconic(hDlg)) break;\r
8057     newSizeX = LOWORD(lParam);\r
8058     newSizeY = HIWORD(lParam);\r
8059     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8060       RECT rectText, rectInput;\r
8061       POINT pt;\r
8062       int newTextHeight, newTextWidth;\r
8063       GetWindowRect(hText, &rectText);\r
8064       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8065       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8066       if (newTextHeight < 0) {\r
8067         newSizeY += -newTextHeight;\r
8068         newTextHeight = 0;\r
8069       }\r
8070       SetWindowPos(hText, NULL, 0, 0,\r
8071         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8072       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8073       pt.x = rectInput.left;\r
8074       pt.y = rectInput.top + newSizeY - sizeY;\r
8075       ScreenToClient(hDlg, &pt);\r
8076       SetWindowPos(hInput, NULL, \r
8077         pt.x, pt.y, /* needs client coords */   \r
8078         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8079         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8080     }\r
8081     sizeX = newSizeX;\r
8082     sizeY = newSizeY;\r
8083     break;\r
8084 \r
8085   case WM_GETMINMAXINFO:\r
8086     /* Prevent resizing window too small */\r
8087     mmi = (MINMAXINFO *) lParam;\r
8088     mmi->ptMinTrackSize.x = 100;\r
8089     mmi->ptMinTrackSize.y = 100;\r
8090     break;\r
8091 \r
8092   /* [AS] Snapping */\r
8093   case WM_ENTERSIZEMOVE:\r
8094     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8095 \r
8096   case WM_SIZING:\r
8097     return OnSizing( &sd, hDlg, wParam, lParam );\r
8098 \r
8099   case WM_MOVING:\r
8100     return OnMoving( &sd, hDlg, wParam, lParam );\r
8101 \r
8102   case WM_EXITSIZEMOVE:\r
8103     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8104   }\r
8105 \r
8106   return DefWindowProc(hDlg, message, wParam, lParam);\r
8107 }\r
8108 \r
8109 \r
8110 VOID\r
8111 ConsoleCreate()\r
8112 {\r
8113   HWND hCons;\r
8114   if (hwndConsole) return;\r
8115   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8116   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8117 }\r
8118 \r
8119 \r
8120 VOID\r
8121 ConsoleOutput(char* data, int length, int forceVisible)\r
8122 {\r
8123   HWND hText;\r
8124   int trim, exlen;\r
8125   char *p, *q;\r
8126   char buf[CO_MAX+1];\r
8127   POINT pEnd;\r
8128   RECT rect;\r
8129   static int delayLF = 0;\r
8130   CHARRANGE savesel, sel;\r
8131 \r
8132   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8133   p = data;\r
8134   q = buf;\r
8135   if (delayLF) {\r
8136     *q++ = '\r';\r
8137     *q++ = '\n';\r
8138     delayLF = 0;\r
8139   }\r
8140   while (length--) {\r
8141     if (*p == '\n') {\r
8142       if (*++p) {\r
8143         *q++ = '\r';\r
8144         *q++ = '\n';\r
8145       } else {\r
8146         delayLF = 1;\r
8147       }\r
8148     } else if (*p == '\007') {\r
8149        MyPlaySound(&sounds[(int)SoundBell]);\r
8150        p++;\r
8151     } else {\r
8152       *q++ = *p++;\r
8153     }\r
8154   }\r
8155   *q = NULLCHAR;\r
8156   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8157   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8158   /* Save current selection */\r
8159   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8160   exlen = GetWindowTextLength(hText);\r
8161   /* Find out whether current end of text is visible */\r
8162   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8163   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8164   /* Trim existing text if it's too long */\r
8165   if (exlen + (q - buf) > CO_MAX) {\r
8166     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8167     sel.cpMin = 0;\r
8168     sel.cpMax = trim;\r
8169     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8170     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8171     exlen -= trim;\r
8172     savesel.cpMin -= trim;\r
8173     savesel.cpMax -= trim;\r
8174     if (exlen < 0) exlen = 0;\r
8175     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8176     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8177   }\r
8178   /* Append the new text */\r
8179   sel.cpMin = exlen;\r
8180   sel.cpMax = exlen;\r
8181   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8182   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8183   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8184   if (forceVisible || exlen == 0 ||\r
8185       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8186        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8187     /* Scroll to make new end of text visible if old end of text\r
8188        was visible or new text is an echo of user typein */\r
8189     sel.cpMin = 9999999;\r
8190     sel.cpMax = 9999999;\r
8191     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8192     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8193     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8194     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8195   }\r
8196   if (savesel.cpMax == exlen || forceVisible) {\r
8197     /* Move insert point to new end of text if it was at the old\r
8198        end of text or if the new text is an echo of user typein */\r
8199     sel.cpMin = 9999999;\r
8200     sel.cpMax = 9999999;\r
8201     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8202   } else {\r
8203     /* Restore previous selection */\r
8204     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8205   }\r
8206   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8207 }\r
8208 \r
8209 /*---------*/\r
8210 \r
8211 \r
8212 void\r
8213 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8214 {\r
8215   char buf[100];\r
8216   char *str;\r
8217   COLORREF oldFg, oldBg;\r
8218   HFONT oldFont;\r
8219   RECT rect;\r
8220 \r
8221   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8222 \r
8223   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8224   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8225   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8226 \r
8227   rect.left = x;\r
8228   rect.right = x + squareSize;\r
8229   rect.top  = y;\r
8230   rect.bottom = y + squareSize;\r
8231   str = buf;\r
8232 \r
8233   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8234                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8235              y, ETO_CLIPPED|ETO_OPAQUE,\r
8236              &rect, str, strlen(str), NULL);\r
8237 \r
8238   (void) SetTextColor(hdc, oldFg);\r
8239   (void) SetBkColor(hdc, oldBg);\r
8240   (void) SelectObject(hdc, oldFont);\r
8241 }\r
8242 \r
8243 void\r
8244 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8245               RECT *rect, char *color, char *flagFell)\r
8246 {\r
8247   char buf[100];\r
8248   char *str;\r
8249   COLORREF oldFg, oldBg;\r
8250   HFONT oldFont;\r
8251 \r
8252   if (appData.clockMode) {\r
8253     if (tinyLayout)\r
8254       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8255     else\r
8256       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8257     str = buf;\r
8258   } else {\r
8259     str = color;\r
8260   }\r
8261 \r
8262   if (highlight) {\r
8263     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8264     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8265   } else {\r
8266     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8267     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8268   }\r
8269   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8270 \r
8271   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8272              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8273              rect, str, strlen(str), NULL);\r
8274 \r
8275   (void) SetTextColor(hdc, oldFg);\r
8276   (void) SetBkColor(hdc, oldBg);\r
8277   (void) SelectObject(hdc, oldFont);\r
8278 }\r
8279 \r
8280 \r
8281 int\r
8282 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8283            OVERLAPPED *ovl)\r
8284 {\r
8285   int ok, err;\r
8286 \r
8287   /* [AS]  */\r
8288   if( count <= 0 ) {\r
8289     if (appData.debugMode) {\r
8290       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8291     }\r
8292 \r
8293     return ERROR_INVALID_USER_BUFFER;\r
8294   }\r
8295 \r
8296   ResetEvent(ovl->hEvent);\r
8297   ovl->Offset = ovl->OffsetHigh = 0;\r
8298   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8299   if (ok) {\r
8300     err = NO_ERROR;\r
8301   } else {\r
8302     err = GetLastError();\r
8303     if (err == ERROR_IO_PENDING) {\r
8304       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8305       if (ok)\r
8306         err = NO_ERROR;\r
8307       else\r
8308         err = GetLastError();\r
8309     }\r
8310   }\r
8311   return err;\r
8312 }\r
8313 \r
8314 int\r
8315 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8316             OVERLAPPED *ovl)\r
8317 {\r
8318   int ok, err;\r
8319 \r
8320   ResetEvent(ovl->hEvent);\r
8321   ovl->Offset = ovl->OffsetHigh = 0;\r
8322   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8323   if (ok) {\r
8324     err = NO_ERROR;\r
8325   } else {\r
8326     err = GetLastError();\r
8327     if (err == ERROR_IO_PENDING) {\r
8328       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8329       if (ok)\r
8330         err = NO_ERROR;\r
8331       else\r
8332         err = GetLastError();\r
8333     }\r
8334   }\r
8335   return err;\r
8336 }\r
8337 \r
8338 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8339 void CheckForInputBufferFull( InputSource * is )\r
8340 {\r
8341     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8342         /* Look for end of line */\r
8343         char * p = is->buf;\r
8344         \r
8345         while( p < is->next && *p != '\n' ) {\r
8346             p++;\r
8347         }\r
8348 \r
8349         if( p >= is->next ) {\r
8350             if (appData.debugMode) {\r
8351                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8352             }\r
8353 \r
8354             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8355             is->count = (DWORD) -1;\r
8356             is->next = is->buf;\r
8357         }\r
8358     }\r
8359 }\r
8360 \r
8361 DWORD\r
8362 InputThread(LPVOID arg)\r
8363 {\r
8364   InputSource *is;\r
8365   OVERLAPPED ovl;\r
8366 \r
8367   is = (InputSource *) arg;\r
8368   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8369   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8370   while (is->hThread != NULL) {\r
8371     is->error = DoReadFile(is->hFile, is->next,\r
8372                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8373                            &is->count, &ovl);\r
8374     if (is->error == NO_ERROR) {\r
8375       is->next += is->count;\r
8376     } else {\r
8377       if (is->error == ERROR_BROKEN_PIPE) {\r
8378         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8379         is->count = 0;\r
8380       } else {\r
8381         is->count = (DWORD) -1;\r
8382         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8383         break; \r
8384       }\r
8385     }\r
8386 \r
8387     CheckForInputBufferFull( is );\r
8388 \r
8389     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8390 \r
8391     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8392 \r
8393     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8394   }\r
8395 \r
8396   CloseHandle(ovl.hEvent);\r
8397   CloseHandle(is->hFile);\r
8398 \r
8399   if (appData.debugMode) {\r
8400     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8401   }\r
8402 \r
8403   return 0;\r
8404 }\r
8405 \r
8406 \r
8407 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8408 DWORD\r
8409 NonOvlInputThread(LPVOID arg)\r
8410 {\r
8411   InputSource *is;\r
8412   char *p, *q;\r
8413   int i;\r
8414   char prev;\r
8415 \r
8416   is = (InputSource *) arg;\r
8417   while (is->hThread != NULL) {\r
8418     is->error = ReadFile(is->hFile, is->next,\r
8419                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8420                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8421     if (is->error == NO_ERROR) {\r
8422       /* Change CRLF to LF */\r
8423       if (is->next > is->buf) {\r
8424         p = is->next - 1;\r
8425         i = is->count + 1;\r
8426       } else {\r
8427         p = is->next;\r
8428         i = is->count;\r
8429       }\r
8430       q = p;\r
8431       prev = NULLCHAR;\r
8432       while (i > 0) {\r
8433         if (prev == '\r' && *p == '\n') {\r
8434           *(q-1) = '\n';\r
8435           is->count--;\r
8436         } else { \r
8437           *q++ = *p;\r
8438         }\r
8439         prev = *p++;\r
8440         i--;\r
8441       }\r
8442       *q = NULLCHAR;\r
8443       is->next = q;\r
8444     } else {\r
8445       if (is->error == ERROR_BROKEN_PIPE) {\r
8446         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8447         is->count = 0; \r
8448       } else {\r
8449         is->count = (DWORD) -1;\r
8450       }\r
8451     }\r
8452 \r
8453     CheckForInputBufferFull( is );\r
8454 \r
8455     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8456 \r
8457     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8458 \r
8459     if (is->count < 0) break;  /* Quit on error */\r
8460   }\r
8461   CloseHandle(is->hFile);\r
8462   return 0;\r
8463 }\r
8464 \r
8465 DWORD\r
8466 SocketInputThread(LPVOID arg)\r
8467 {\r
8468   InputSource *is;\r
8469 \r
8470   is = (InputSource *) arg;\r
8471   while (is->hThread != NULL) {\r
8472     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8473     if ((int)is->count == SOCKET_ERROR) {\r
8474       is->count = (DWORD) -1;\r
8475       is->error = WSAGetLastError();\r
8476     } else {\r
8477       is->error = NO_ERROR;\r
8478       is->next += is->count;\r
8479       if (is->count == 0 && is->second == is) {\r
8480         /* End of file on stderr; quit with no message */\r
8481         break;\r
8482       }\r
8483     }\r
8484     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8485 \r
8486     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8487 \r
8488     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8489   }\r
8490   return 0;\r
8491 }\r
8492 \r
8493 VOID\r
8494 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8495 {\r
8496   InputSource *is;\r
8497 \r
8498   is = (InputSource *) lParam;\r
8499   if (is->lineByLine) {\r
8500     /* Feed in lines one by one */\r
8501     char *p = is->buf;\r
8502     char *q = p;\r
8503     while (q < is->next) {\r
8504       if (*q++ == '\n') {\r
8505         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8506         p = q;\r
8507       }\r
8508     }\r
8509     \r
8510     /* Move any partial line to the start of the buffer */\r
8511     q = is->buf;\r
8512     while (p < is->next) {\r
8513       *q++ = *p++;\r
8514     }\r
8515     is->next = q;\r
8516 \r
8517     if (is->error != NO_ERROR || is->count == 0) {\r
8518       /* Notify backend of the error.  Note: If there was a partial\r
8519          line at the end, it is not flushed through. */\r
8520       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8521     }\r
8522   } else {\r
8523     /* Feed in the whole chunk of input at once */\r
8524     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8525     is->next = is->buf;\r
8526   }\r
8527 }\r
8528 \r
8529 /*---------------------------------------------------------------------------*\\r
8530  *\r
8531  *  Menu enables. Used when setting various modes.\r
8532  *\r
8533 \*---------------------------------------------------------------------------*/\r
8534 \r
8535 typedef struct {\r
8536   int item;\r
8537   int flags;\r
8538 } Enables;\r
8539 \r
8540 VOID\r
8541 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8542 {\r
8543   while (enab->item > 0) {\r
8544     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8545     enab++;\r
8546   }\r
8547 }\r
8548 \r
8549 Enables gnuEnables[] = {\r
8550   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8560   { -1, -1 }\r
8561 };\r
8562 \r
8563 Enables icsEnables[] = {\r
8564   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8570   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8576   { -1, -1 }\r
8577 };\r
8578 \r
8579 #ifdef ZIPPY\r
8580 Enables zippyEnables[] = {\r
8581   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8582   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8583   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8584   { -1, -1 }\r
8585 };\r
8586 #endif\r
8587 \r
8588 Enables ncpEnables[] = {\r
8589   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8597   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8598   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8604   { -1, -1 }\r
8605 };\r
8606 \r
8607 Enables trainingOnEnables[] = {\r
8608   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8616   { -1, -1 }\r
8617 };\r
8618 \r
8619 Enables trainingOffEnables[] = {\r
8620   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8621   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8622   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8623   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8624   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8628   { -1, -1 }\r
8629 };\r
8630 \r
8631 /* These modify either ncpEnables or gnuEnables */\r
8632 Enables cmailEnables[] = {\r
8633   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8635   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8636   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8639   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8640   { -1, -1 }\r
8641 };\r
8642 \r
8643 Enables machineThinkingEnables[] = {\r
8644   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8652   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8659   { -1, -1 }\r
8660 };\r
8661 \r
8662 Enables userThinkingEnables[] = {\r
8663   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8664   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8665   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8666   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8667   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8668   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8670   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8671   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8672   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8675   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8676   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8677   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8678   { -1, -1 }\r
8679 };\r
8680 \r
8681 /*---------------------------------------------------------------------------*\\r
8682  *\r
8683  *  Front-end interface functions exported by XBoard.\r
8684  *  Functions appear in same order as prototypes in frontend.h.\r
8685  * \r
8686 \*---------------------------------------------------------------------------*/\r
8687 VOID\r
8688 ModeHighlight()\r
8689 {\r
8690   static UINT prevChecked = 0;\r
8691   static int prevPausing = 0;\r
8692   UINT nowChecked;\r
8693 \r
8694   if (pausing != prevPausing) {\r
8695     prevPausing = pausing;\r
8696     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8697                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8698     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8699   }\r
8700 \r
8701   switch (gameMode) {\r
8702   case BeginningOfGame:\r
8703     if (appData.icsActive)\r
8704       nowChecked = IDM_IcsClient;\r
8705     else if (appData.noChessProgram)\r
8706       nowChecked = IDM_EditGame;\r
8707     else\r
8708       nowChecked = IDM_MachineBlack;\r
8709     break;\r
8710   case MachinePlaysBlack:\r
8711     nowChecked = IDM_MachineBlack;\r
8712     break;\r
8713   case MachinePlaysWhite:\r
8714     nowChecked = IDM_MachineWhite;\r
8715     break;\r
8716   case TwoMachinesPlay:\r
8717     nowChecked = IDM_TwoMachines;\r
8718     break;\r
8719   case AnalyzeMode:\r
8720     nowChecked = IDM_AnalysisMode;\r
8721     break;\r
8722   case AnalyzeFile:\r
8723     nowChecked = IDM_AnalyzeFile;\r
8724     break;\r
8725   case EditGame:\r
8726     nowChecked = IDM_EditGame;\r
8727     break;\r
8728   case PlayFromGameFile:\r
8729     nowChecked = IDM_LoadGame;\r
8730     break;\r
8731   case EditPosition:\r
8732     nowChecked = IDM_EditPosition;\r
8733     break;\r
8734   case Training:\r
8735     nowChecked = IDM_Training;\r
8736     break;\r
8737   case IcsPlayingWhite:\r
8738   case IcsPlayingBlack:\r
8739   case IcsObserving:\r
8740   case IcsIdle:\r
8741     nowChecked = IDM_IcsClient;\r
8742     break;\r
8743   default:\r
8744   case EndOfGame:\r
8745     nowChecked = 0;\r
8746     break;\r
8747   }\r
8748   if (prevChecked != 0)\r
8749     (void) CheckMenuItem(GetMenu(hwndMain),\r
8750                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8751   if (nowChecked != 0)\r
8752     (void) CheckMenuItem(GetMenu(hwndMain),\r
8753                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8754 \r
8755   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8756     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8757                           MF_BYCOMMAND|MF_ENABLED);\r
8758   } else {\r
8759     (void) EnableMenuItem(GetMenu(hwndMain), \r
8760                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8761   }\r
8762 \r
8763   prevChecked = nowChecked;\r
8764 \r
8765   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8766   if (appData.icsActive) {\r
8767        if (appData.icsEngineAnalyze) {\r
8768                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8769                        MF_BYCOMMAND|MF_CHECKED);\r
8770        } else {\r
8771                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8772                        MF_BYCOMMAND|MF_UNCHECKED);\r
8773        }\r
8774   }\r
8775 }\r
8776 \r
8777 VOID\r
8778 SetICSMode()\r
8779 {\r
8780   HMENU hmenu = GetMenu(hwndMain);\r
8781   SetMenuEnables(hmenu, icsEnables);\r
8782   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8783     MF_BYPOSITION|MF_ENABLED);\r
8784 #ifdef ZIPPY\r
8785   if (appData.zippyPlay) {\r
8786     SetMenuEnables(hmenu, zippyEnables);\r
8787     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8788          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8789           MF_BYCOMMAND|MF_ENABLED);\r
8790   }\r
8791 #endif\r
8792 }\r
8793 \r
8794 VOID\r
8795 SetGNUMode()\r
8796 {\r
8797   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8798 }\r
8799 \r
8800 VOID\r
8801 SetNCPMode()\r
8802 {\r
8803   HMENU hmenu = GetMenu(hwndMain);\r
8804   SetMenuEnables(hmenu, ncpEnables);\r
8805   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8806     MF_BYPOSITION|MF_GRAYED);\r
8807     DrawMenuBar(hwndMain);\r
8808 }\r
8809 \r
8810 VOID\r
8811 SetCmailMode()\r
8812 {\r
8813   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8814 }\r
8815 \r
8816 VOID \r
8817 SetTrainingModeOn()\r
8818 {\r
8819   int i;\r
8820   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8821   for (i = 0; i < N_BUTTONS; i++) {\r
8822     if (buttonDesc[i].hwnd != NULL)\r
8823       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8824   }\r
8825   CommentPopDown();\r
8826 }\r
8827 \r
8828 VOID SetTrainingModeOff()\r
8829 {\r
8830   int i;\r
8831   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8832   for (i = 0; i < N_BUTTONS; i++) {\r
8833     if (buttonDesc[i].hwnd != NULL)\r
8834       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8835   }\r
8836 }\r
8837 \r
8838 \r
8839 VOID\r
8840 SetUserThinkingEnables()\r
8841 {\r
8842   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8843 }\r
8844 \r
8845 VOID\r
8846 SetMachineThinkingEnables()\r
8847 {\r
8848   HMENU hMenu = GetMenu(hwndMain);\r
8849   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8850 \r
8851   SetMenuEnables(hMenu, machineThinkingEnables);\r
8852 \r
8853   if (gameMode == MachinePlaysBlack) {\r
8854     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8855   } else if (gameMode == MachinePlaysWhite) {\r
8856     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8857   } else if (gameMode == TwoMachinesPlay) {\r
8858     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8859   }\r
8860 }\r
8861 \r
8862 \r
8863 VOID\r
8864 DisplayTitle(char *str)\r
8865 {\r
8866   char title[MSG_SIZ], *host;\r
8867   if (str[0] != NULLCHAR) {\r
8868     strcpy(title, str);\r
8869   } else if (appData.icsActive) {\r
8870     if (appData.icsCommPort[0] != NULLCHAR)\r
8871       host = "ICS";\r
8872     else \r
8873       host = appData.icsHost;\r
8874     sprintf(title, "%s: %s", szTitle, host);\r
8875   } else if (appData.noChessProgram) {\r
8876     strcpy(title, szTitle);\r
8877   } else {\r
8878     strcpy(title, szTitle);\r
8879     strcat(title, ": ");\r
8880     strcat(title, first.tidy);\r
8881   }\r
8882   SetWindowText(hwndMain, title);\r
8883 }\r
8884 \r
8885 \r
8886 VOID\r
8887 DisplayMessage(char *str1, char *str2)\r
8888 {\r
8889   HDC hdc;\r
8890   HFONT oldFont;\r
8891   int remain = MESSAGE_TEXT_MAX - 1;\r
8892   int len;\r
8893 \r
8894   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8895   messageText[0] = NULLCHAR;\r
8896   if (*str1) {\r
8897     len = strlen(str1);\r
8898     if (len > remain) len = remain;\r
8899     strncpy(messageText, str1, len);\r
8900     messageText[len] = NULLCHAR;\r
8901     remain -= len;\r
8902   }\r
8903   if (*str2 && remain >= 2) {\r
8904     if (*str1) {\r
8905       strcat(messageText, "  ");\r
8906       remain -= 2;\r
8907     }\r
8908     len = strlen(str2);\r
8909     if (len > remain) len = remain;\r
8910     strncat(messageText, str2, len);\r
8911   }\r
8912   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8913 \r
8914   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8915   hdc = GetDC(hwndMain);\r
8916   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8917   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8918              &messageRect, messageText, strlen(messageText), NULL);\r
8919   (void) SelectObject(hdc, oldFont);\r
8920   (void) ReleaseDC(hwndMain, hdc);\r
8921 }\r
8922 \r
8923 VOID\r
8924 DisplayError(char *str, int error)\r
8925 {\r
8926   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8927   int len;\r
8928 \r
8929   if (error == 0) {\r
8930     strcpy(buf, str);\r
8931   } else {\r
8932     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8933                         NULL, error, LANG_NEUTRAL,\r
8934                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8935     if (len > 0) {\r
8936       sprintf(buf, "%s:\n%s", str, buf2);\r
8937     } else {\r
8938       ErrorMap *em = errmap;\r
8939       while (em->err != 0 && em->err != error) em++;\r
8940       if (em->err != 0) {\r
8941         sprintf(buf, "%s:\n%s", str, em->msg);\r
8942       } else {\r
8943         sprintf(buf, "%s:\nError code %d", str, error);\r
8944       }\r
8945     }\r
8946   }\r
8947   \r
8948   ErrorPopUp("Error", buf);\r
8949 }\r
8950 \r
8951 \r
8952 VOID\r
8953 DisplayMoveError(char *str)\r
8954 {\r
8955   fromX = fromY = -1;\r
8956   ClearHighlights();\r
8957   DrawPosition(FALSE, NULL);\r
8958   if (appData.popupMoveErrors) {\r
8959     ErrorPopUp("Error", str);\r
8960   } else {\r
8961     DisplayMessage(str, "");\r
8962     moveErrorMessageUp = TRUE;\r
8963   }\r
8964 }\r
8965 \r
8966 VOID\r
8967 DisplayFatalError(char *str, int error, int exitStatus)\r
8968 {\r
8969   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8970   int len;\r
8971   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8972 \r
8973   if (error != 0) {\r
8974     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8975                         NULL, error, LANG_NEUTRAL,\r
8976                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8977     if (len > 0) {\r
8978       sprintf(buf, "%s:\n%s", str, buf2);\r
8979     } else {\r
8980       ErrorMap *em = errmap;\r
8981       while (em->err != 0 && em->err != error) em++;\r
8982       if (em->err != 0) {\r
8983         sprintf(buf, "%s:\n%s", str, em->msg);\r
8984       } else {\r
8985         sprintf(buf, "%s:\nError code %d", str, error);\r
8986       }\r
8987     }\r
8988     str = buf;\r
8989   }\r
8990   if (appData.debugMode) {\r
8991     fprintf(debugFP, "%s: %s\n", label, str);\r
8992   }\r
8993   if (appData.popupExitMessage) {\r
8994     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8995                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8996   }\r
8997   ExitEvent(exitStatus);\r
8998 }\r
8999 \r
9000 \r
9001 VOID\r
9002 DisplayInformation(char *str)\r
9003 {\r
9004   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9005 }\r
9006 \r
9007 \r
9008 VOID\r
9009 DisplayNote(char *str)\r
9010 {\r
9011   ErrorPopUp("Note", str);\r
9012 }\r
9013 \r
9014 \r
9015 typedef struct {\r
9016   char *title, *question, *replyPrefix;\r
9017   ProcRef pr;\r
9018 } QuestionParams;\r
9019 \r
9020 LRESULT CALLBACK\r
9021 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9022 {\r
9023   static QuestionParams *qp;\r
9024   char reply[MSG_SIZ];\r
9025   int len, err;\r
9026 \r
9027   switch (message) {\r
9028   case WM_INITDIALOG:\r
9029     qp = (QuestionParams *) lParam;\r
9030     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9031     SetWindowText(hDlg, qp->title);\r
9032     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9033     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9034     return FALSE;\r
9035 \r
9036   case WM_COMMAND:\r
9037     switch (LOWORD(wParam)) {\r
9038     case IDOK:\r
9039       strcpy(reply, qp->replyPrefix);\r
9040       if (*reply) strcat(reply, " ");\r
9041       len = strlen(reply);\r
9042       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9043       strcat(reply, "\n");\r
9044       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9045       EndDialog(hDlg, TRUE);\r
9046       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9047       return TRUE;\r
9048     case IDCANCEL:\r
9049       EndDialog(hDlg, FALSE);\r
9050       return TRUE;\r
9051     default:\r
9052       break;\r
9053     }\r
9054     break;\r
9055   }\r
9056   return FALSE;\r
9057 }\r
9058 \r
9059 VOID\r
9060 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9061 {\r
9062     QuestionParams qp;\r
9063     FARPROC lpProc;\r
9064     \r
9065     qp.title = title;\r
9066     qp.question = question;\r
9067     qp.replyPrefix = replyPrefix;\r
9068     qp.pr = pr;\r
9069     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9070     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9071       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9072     FreeProcInstance(lpProc);\r
9073 }\r
9074 \r
9075 /* [AS] Pick FRC position */\r
9076 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9077 {\r
9078     static int * lpIndexFRC;\r
9079     BOOL index_is_ok;\r
9080     char buf[16];\r
9081 \r
9082     switch( message )\r
9083     {\r
9084     case WM_INITDIALOG:\r
9085         lpIndexFRC = (int *) lParam;\r
9086 \r
9087         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9088 \r
9089         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9090         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9091         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9092         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9093 \r
9094         break;\r
9095 \r
9096     case WM_COMMAND:\r
9097         switch( LOWORD(wParam) ) {\r
9098         case IDOK:\r
9099             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9100             EndDialog( hDlg, 0 );\r
9101             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9102             return TRUE;\r
9103         case IDCANCEL:\r
9104             EndDialog( hDlg, 1 );   \r
9105             return TRUE;\r
9106         case IDC_NFG_Edit:\r
9107             if( HIWORD(wParam) == EN_CHANGE ) {\r
9108                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9109 \r
9110                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9111             }\r
9112             return TRUE;\r
9113         case IDC_NFG_Random:\r
9114             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9115             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9116             return TRUE;\r
9117         }\r
9118 \r
9119         break;\r
9120     }\r
9121 \r
9122     return FALSE;\r
9123 }\r
9124 \r
9125 int NewGameFRC()\r
9126 {\r
9127     int result;\r
9128     int index = appData.defaultFrcPosition;\r
9129     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9130 \r
9131     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9132 \r
9133     if( result == 0 ) {\r
9134         appData.defaultFrcPosition = index;\r
9135     }\r
9136 \r
9137     return result;\r
9138 }\r
9139 \r
9140 /* [AS] Game list options */\r
9141 typedef struct {\r
9142     char id;\r
9143     char * name;\r
9144 } GLT_Item;\r
9145 \r
9146 static GLT_Item GLT_ItemInfo[] = {\r
9147     { GLT_EVENT,      "Event" },\r
9148     { GLT_SITE,       "Site" },\r
9149     { GLT_DATE,       "Date" },\r
9150     { GLT_ROUND,      "Round" },\r
9151     { GLT_PLAYERS,    "Players" },\r
9152     { GLT_RESULT,     "Result" },\r
9153     { GLT_WHITE_ELO,  "White Rating" },\r
9154     { GLT_BLACK_ELO,  "Black Rating" },\r
9155     { GLT_TIME_CONTROL,"Time Control" },\r
9156     { GLT_VARIANT,    "Variant" },\r
9157     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9158     { 0, 0 }\r
9159 };\r
9160 \r
9161 const char * GLT_FindItem( char id )\r
9162 {\r
9163     const char * result = 0;\r
9164 \r
9165     GLT_Item * list = GLT_ItemInfo;\r
9166 \r
9167     while( list->id != 0 ) {\r
9168         if( list->id == id ) {\r
9169             result = list->name;\r
9170             break;\r
9171         }\r
9172 \r
9173         list++;\r
9174     }\r
9175 \r
9176     return result;\r
9177 }\r
9178 \r
9179 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9180 {\r
9181     const char * name = GLT_FindItem( id );\r
9182 \r
9183     if( name != 0 ) {\r
9184         if( index >= 0 ) {\r
9185             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9186         }\r
9187         else {\r
9188             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9189         }\r
9190     }\r
9191 }\r
9192 \r
9193 void GLT_TagsToList( HWND hDlg, char * tags )\r
9194 {\r
9195     char * pc = tags;\r
9196 \r
9197     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9198 \r
9199     while( *pc ) {\r
9200         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9201         pc++;\r
9202     }\r
9203 \r
9204     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9205 \r
9206     pc = GLT_ALL_TAGS;\r
9207 \r
9208     while( *pc ) {\r
9209         if( strchr( tags, *pc ) == 0 ) {\r
9210             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9211         }\r
9212         pc++;\r
9213     }\r
9214 \r
9215     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9216 }\r
9217 \r
9218 char GLT_ListItemToTag( HWND hDlg, int index )\r
9219 {\r
9220     char result = '\0';\r
9221     char name[128];\r
9222 \r
9223     GLT_Item * list = GLT_ItemInfo;\r
9224 \r
9225     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9226         while( list->id != 0 ) {\r
9227             if( strcmp( list->name, name ) == 0 ) {\r
9228                 result = list->id;\r
9229                 break;\r
9230             }\r
9231 \r
9232             list++;\r
9233         }\r
9234     }\r
9235 \r
9236     return result;\r
9237 }\r
9238 \r
9239 void GLT_MoveSelection( HWND hDlg, int delta )\r
9240 {\r
9241     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9242     int idx2 = idx1 + delta;\r
9243     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9244 \r
9245     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9246         char buf[128];\r
9247 \r
9248         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9249         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9250         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9251         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9252     }\r
9253 }\r
9254 \r
9255 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9256 {\r
9257     static char glt[64];\r
9258     static char * lpUserGLT;\r
9259 \r
9260     switch( message )\r
9261     {\r
9262     case WM_INITDIALOG:\r
9263         lpUserGLT = (char *) lParam;\r
9264         \r
9265         strcpy( glt, lpUserGLT );\r
9266 \r
9267         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9268 \r
9269         /* Initialize list */\r
9270         GLT_TagsToList( hDlg, glt );\r
9271 \r
9272         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9273 \r
9274         break;\r
9275 \r
9276     case WM_COMMAND:\r
9277         switch( LOWORD(wParam) ) {\r
9278         case IDOK:\r
9279             {\r
9280                 char * pc = lpUserGLT;\r
9281                 int idx = 0;\r
9282 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9283                 char id;\r
9284 \r
9285                 do {\r
9286                     id = GLT_ListItemToTag( hDlg, idx );\r
9287 \r
9288                     *pc++ = id;\r
9289                     idx++;\r
9290                 } while( id != '\0' );\r
9291             }\r
9292             EndDialog( hDlg, 0 );\r
9293             return TRUE;\r
9294         case IDCANCEL:\r
9295             EndDialog( hDlg, 1 );\r
9296             return TRUE;\r
9297 \r
9298         case IDC_GLT_Default:\r
9299             strcpy( glt, GLT_DEFAULT_TAGS );\r
9300             GLT_TagsToList( hDlg, glt );\r
9301             return TRUE;\r
9302 \r
9303         case IDC_GLT_Restore:\r
9304             strcpy( glt, lpUserGLT );\r
9305             GLT_TagsToList( hDlg, glt );\r
9306             return TRUE;\r
9307 \r
9308         case IDC_GLT_Up:\r
9309             GLT_MoveSelection( hDlg, -1 );\r
9310             return TRUE;\r
9311 \r
9312         case IDC_GLT_Down:\r
9313             GLT_MoveSelection( hDlg, +1 );\r
9314             return TRUE;\r
9315         }\r
9316 \r
9317         break;\r
9318     }\r
9319 \r
9320     return FALSE;\r
9321 }\r
9322 \r
9323 int GameListOptions()\r
9324 {\r
9325     char glt[64];\r
9326     int result;\r
9327     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9328 \r
9329     strcpy( glt, appData.gameListTags );\r
9330 \r
9331     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9332 \r
9333     if( result == 0 ) {\r
9334         /* [AS] Memory leak here! */\r
9335         appData.gameListTags = strdup( glt ); \r
9336     }\r
9337 \r
9338     return result;\r
9339 }\r
9340 \r
9341 \r
9342 VOID\r
9343 DisplayIcsInteractionTitle(char *str)\r
9344 {\r
9345   char consoleTitle[MSG_SIZ];\r
9346 \r
9347   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9348   SetWindowText(hwndConsole, consoleTitle);\r
9349 }\r
9350 \r
9351 void\r
9352 DrawPosition(int fullRedraw, Board board)\r
9353 {\r
9354   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9355 }\r
9356 \r
9357 \r
9358 VOID\r
9359 ResetFrontEnd()\r
9360 {\r
9361   fromX = fromY = -1;\r
9362   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9363     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9364     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9365     dragInfo.lastpos = dragInfo.pos;\r
9366     dragInfo.start.x = dragInfo.start.y = -1;\r
9367     dragInfo.from = dragInfo.start;\r
9368     ReleaseCapture();\r
9369     DrawPosition(TRUE, NULL);\r
9370   }\r
9371 }\r
9372 \r
9373 \r
9374 VOID\r
9375 CommentPopUp(char *title, char *str)\r
9376 {\r
9377   HWND hwnd = GetActiveWindow();\r
9378   EitherCommentPopUp(0, title, str, FALSE);\r
9379   SetActiveWindow(hwnd);\r
9380 }\r
9381 \r
9382 VOID\r
9383 CommentPopDown(void)\r
9384 {\r
9385   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9386   if (commentDialog) {\r
9387     ShowWindow(commentDialog, SW_HIDE);\r
9388   }\r
9389   commentDialogUp = FALSE;\r
9390 }\r
9391 \r
9392 VOID\r
9393 EditCommentPopUp(int index, char *title, char *str)\r
9394 {\r
9395   EitherCommentPopUp(index, title, str, TRUE);\r
9396 }\r
9397 \r
9398 \r
9399 VOID\r
9400 RingBell()\r
9401 {\r
9402   MyPlaySound(&sounds[(int)SoundMove]);\r
9403 }\r
9404 \r
9405 VOID PlayIcsWinSound()\r
9406 {\r
9407   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9408 }\r
9409 \r
9410 VOID PlayIcsLossSound()\r
9411 {\r
9412   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9413 }\r
9414 \r
9415 VOID PlayIcsDrawSound()\r
9416 {\r
9417   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9418 }\r
9419 \r
9420 VOID PlayIcsUnfinishedSound()\r
9421 {\r
9422   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9423 }\r
9424 \r
9425 VOID\r
9426 PlayAlarmSound()\r
9427 {\r
9428   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9429 }\r
9430 \r
9431 \r
9432 VOID\r
9433 EchoOn()\r
9434 {\r
9435   HWND hInput;\r
9436   consoleEcho = TRUE;\r
9437   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9438   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9439   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9440 }\r
9441 \r
9442 \r
9443 VOID\r
9444 EchoOff()\r
9445 {\r
9446   CHARFORMAT cf;\r
9447   HWND hInput;\r
9448   consoleEcho = FALSE;\r
9449   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9450   /* This works OK: set text and background both to the same color */\r
9451   cf = consoleCF;\r
9452   cf.crTextColor = COLOR_ECHOOFF;\r
9453   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9454   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9455 }\r
9456 \r
9457 /* No Raw()...? */\r
9458 \r
9459 void Colorize(ColorClass cc, int continuation)\r
9460 {\r
9461   currentColorClass = cc;\r
9462   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9463   consoleCF.crTextColor = textAttribs[cc].color;\r
9464   consoleCF.dwEffects = textAttribs[cc].effects;\r
9465   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9466 }\r
9467 \r
9468 char *\r
9469 UserName()\r
9470 {\r
9471   static char buf[MSG_SIZ];\r
9472   DWORD bufsiz = MSG_SIZ;\r
9473 \r
9474   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9475         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9476   }\r
9477   if (!GetUserName(buf, &bufsiz)) {\r
9478     /*DisplayError("Error getting user name", GetLastError());*/\r
9479     strcpy(buf, "User");\r
9480   }\r
9481   return buf;\r
9482 }\r
9483 \r
9484 char *\r
9485 HostName()\r
9486 {\r
9487   static char buf[MSG_SIZ];\r
9488   DWORD bufsiz = MSG_SIZ;\r
9489 \r
9490   if (!GetComputerName(buf, &bufsiz)) {\r
9491     /*DisplayError("Error getting host name", GetLastError());*/\r
9492     strcpy(buf, "Unknown");\r
9493   }\r
9494   return buf;\r
9495 }\r
9496 \r
9497 \r
9498 int\r
9499 ClockTimerRunning()\r
9500 {\r
9501   return clockTimerEvent != 0;\r
9502 }\r
9503 \r
9504 int\r
9505 StopClockTimer()\r
9506 {\r
9507   if (clockTimerEvent == 0) return FALSE;\r
9508   KillTimer(hwndMain, clockTimerEvent);\r
9509   clockTimerEvent = 0;\r
9510   return TRUE;\r
9511 }\r
9512 \r
9513 void\r
9514 StartClockTimer(long millisec)\r
9515 {\r
9516   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9517                              (UINT) millisec, NULL);\r
9518 }\r
9519 \r
9520 void\r
9521 DisplayWhiteClock(long timeRemaining, int highlight)\r
9522 {\r
9523   HDC hdc;\r
9524   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9525 \r
9526   if(appData.noGUI) return;\r
9527   hdc = GetDC(hwndMain);\r
9528   if (!IsIconic(hwndMain)) {\r
9529     DisplayAClock(hdc, timeRemaining, highlight, \r
9530                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9531   }\r
9532   if (highlight && iconCurrent == iconBlack) {\r
9533     iconCurrent = iconWhite;\r
9534     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9535     if (IsIconic(hwndMain)) {\r
9536       DrawIcon(hdc, 2, 2, iconCurrent);\r
9537     }\r
9538   }\r
9539   (void) ReleaseDC(hwndMain, hdc);\r
9540   if (hwndConsole)\r
9541     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9542 }\r
9543 \r
9544 void\r
9545 DisplayBlackClock(long timeRemaining, int highlight)\r
9546 {\r
9547   HDC hdc;\r
9548   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9549 \r
9550   if(appData.noGUI) return;\r
9551   hdc = GetDC(hwndMain);\r
9552   if (!IsIconic(hwndMain)) {\r
9553     DisplayAClock(hdc, timeRemaining, highlight, \r
9554                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9555   }\r
9556   if (highlight && iconCurrent == iconWhite) {\r
9557     iconCurrent = iconBlack;\r
9558     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9559     if (IsIconic(hwndMain)) {\r
9560       DrawIcon(hdc, 2, 2, iconCurrent);\r
9561     }\r
9562   }\r
9563   (void) ReleaseDC(hwndMain, hdc);\r
9564   if (hwndConsole)\r
9565     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9566 }\r
9567 \r
9568 \r
9569 int\r
9570 LoadGameTimerRunning()\r
9571 {\r
9572   return loadGameTimerEvent != 0;\r
9573 }\r
9574 \r
9575 int\r
9576 StopLoadGameTimer()\r
9577 {\r
9578   if (loadGameTimerEvent == 0) return FALSE;\r
9579   KillTimer(hwndMain, loadGameTimerEvent);\r
9580   loadGameTimerEvent = 0;\r
9581   return TRUE;\r
9582 }\r
9583 \r
9584 void\r
9585 StartLoadGameTimer(long millisec)\r
9586 {\r
9587   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9588                                 (UINT) millisec, NULL);\r
9589 }\r
9590 \r
9591 void\r
9592 AutoSaveGame()\r
9593 {\r
9594   char *defName;\r
9595   FILE *f;\r
9596   char fileTitle[MSG_SIZ];\r
9597 \r
9598   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9599   f = OpenFileDialog(hwndMain, "a", defName,\r
9600                      appData.oldSaveStyle ? "gam" : "pgn",\r
9601                      GAME_FILT, \r
9602                      "Save Game to File", NULL, fileTitle, NULL);\r
9603   if (f != NULL) {\r
9604     SaveGame(f, 0, "");\r
9605     fclose(f);\r
9606   }\r
9607 }\r
9608 \r
9609 \r
9610 void\r
9611 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9612 {\r
9613   if (delayedTimerEvent != 0) {\r
9614     if (appData.debugMode) {\r
9615       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9616     }\r
9617     KillTimer(hwndMain, delayedTimerEvent);\r
9618     delayedTimerEvent = 0;\r
9619     delayedTimerCallback();\r
9620   }\r
9621   delayedTimerCallback = cb;\r
9622   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9623                                 (UINT) millisec, NULL);\r
9624 }\r
9625 \r
9626 DelayedEventCallback\r
9627 GetDelayedEvent()\r
9628 {\r
9629   if (delayedTimerEvent) {\r
9630     return delayedTimerCallback;\r
9631   } else {\r
9632     return NULL;\r
9633   }\r
9634 }\r
9635 \r
9636 void\r
9637 CancelDelayedEvent()\r
9638 {\r
9639   if (delayedTimerEvent) {\r
9640     KillTimer(hwndMain, delayedTimerEvent);\r
9641     delayedTimerEvent = 0;\r
9642   }\r
9643 }\r
9644 \r
9645 DWORD GetWin32Priority(int nice)\r
9646 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9647 /*\r
9648 REALTIME_PRIORITY_CLASS     0x00000100\r
9649 HIGH_PRIORITY_CLASS         0x00000080\r
9650 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9651 NORMAL_PRIORITY_CLASS       0x00000020\r
9652 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9653 IDLE_PRIORITY_CLASS         0x00000040\r
9654 */\r
9655         if (nice < -15) return 0x00000080;\r
9656         if (nice < 0)   return 0x00008000;\r
9657         if (nice == 0)  return 0x00000020;\r
9658         if (nice < 15)  return 0x00004000;\r
9659         return 0x00000040;\r
9660 }\r
9661 \r
9662 /* Start a child process running the given program.\r
9663    The process's standard output can be read from "from", and its\r
9664    standard input can be written to "to".\r
9665    Exit with fatal error if anything goes wrong.\r
9666    Returns an opaque pointer that can be used to destroy the process\r
9667    later.\r
9668 */\r
9669 int\r
9670 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9671 {\r
9672 #define BUFSIZE 4096\r
9673 \r
9674   HANDLE hChildStdinRd, hChildStdinWr,\r
9675     hChildStdoutRd, hChildStdoutWr;\r
9676   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9677   SECURITY_ATTRIBUTES saAttr;\r
9678   BOOL fSuccess;\r
9679   PROCESS_INFORMATION piProcInfo;\r
9680   STARTUPINFO siStartInfo;\r
9681   ChildProc *cp;\r
9682   char buf[MSG_SIZ];\r
9683   DWORD err;\r
9684 \r
9685   if (appData.debugMode) {\r
9686     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9687   }\r
9688 \r
9689   *pr = NoProc;\r
9690 \r
9691   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9692   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9693   saAttr.bInheritHandle = TRUE;\r
9694   saAttr.lpSecurityDescriptor = NULL;\r
9695 \r
9696   /*\r
9697    * The steps for redirecting child's STDOUT:\r
9698    *     1. Create anonymous pipe to be STDOUT for child.\r
9699    *     2. Create a noninheritable duplicate of read handle,\r
9700    *         and close the inheritable read handle.\r
9701    */\r
9702 \r
9703   /* Create a pipe for the child's STDOUT. */\r
9704   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9705     return GetLastError();\r
9706   }\r
9707 \r
9708   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9709   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9710                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9711                              FALSE,     /* not inherited */\r
9712                              DUPLICATE_SAME_ACCESS);\r
9713   if (! fSuccess) {\r
9714     return GetLastError();\r
9715   }\r
9716   CloseHandle(hChildStdoutRd);\r
9717 \r
9718   /*\r
9719    * The steps for redirecting child's STDIN:\r
9720    *     1. Create anonymous pipe to be STDIN for child.\r
9721    *     2. Create a noninheritable duplicate of write handle,\r
9722    *         and close the inheritable write handle.\r
9723    */\r
9724 \r
9725   /* Create a pipe for the child's STDIN. */\r
9726   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9727     return GetLastError();\r
9728   }\r
9729 \r
9730   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9731   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9732                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9733                              FALSE,     /* not inherited */\r
9734                              DUPLICATE_SAME_ACCESS);\r
9735   if (! fSuccess) {\r
9736     return GetLastError();\r
9737   }\r
9738   CloseHandle(hChildStdinWr);\r
9739 \r
9740   /* Arrange to (1) look in dir for the child .exe file, and\r
9741    * (2) have dir be the child's working directory.  Interpret\r
9742    * dir relative to the directory WinBoard loaded from. */\r
9743   GetCurrentDirectory(MSG_SIZ, buf);\r
9744   SetCurrentDirectory(installDir);\r
9745   SetCurrentDirectory(dir);\r
9746 \r
9747   /* Now create the child process. */\r
9748 \r
9749   siStartInfo.cb = sizeof(STARTUPINFO);\r
9750   siStartInfo.lpReserved = NULL;\r
9751   siStartInfo.lpDesktop = NULL;\r
9752   siStartInfo.lpTitle = NULL;\r
9753   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9754   siStartInfo.cbReserved2 = 0;\r
9755   siStartInfo.lpReserved2 = NULL;\r
9756   siStartInfo.hStdInput = hChildStdinRd;\r
9757   siStartInfo.hStdOutput = hChildStdoutWr;\r
9758   siStartInfo.hStdError = hChildStdoutWr;\r
9759 \r
9760   fSuccess = CreateProcess(NULL,\r
9761                            cmdLine,        /* command line */\r
9762                            NULL,           /* process security attributes */\r
9763                            NULL,           /* primary thread security attrs */\r
9764                            TRUE,           /* handles are inherited */\r
9765                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9766                            NULL,           /* use parent's environment */\r
9767                            NULL,\r
9768                            &siStartInfo, /* STARTUPINFO pointer */\r
9769                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9770 \r
9771   err = GetLastError();\r
9772   SetCurrentDirectory(buf); /* return to prev directory */\r
9773   if (! fSuccess) {\r
9774     return err;\r
9775   }\r
9776 \r
9777   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9778     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9779     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9780   }\r
9781 \r
9782   /* Close the handles we don't need in the parent */\r
9783   CloseHandle(piProcInfo.hThread);\r
9784   CloseHandle(hChildStdinRd);\r
9785   CloseHandle(hChildStdoutWr);\r
9786 \r
9787   /* Prepare return value */\r
9788   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9789   cp->kind = CPReal;\r
9790   cp->hProcess = piProcInfo.hProcess;\r
9791   cp->pid = piProcInfo.dwProcessId;\r
9792   cp->hFrom = hChildStdoutRdDup;\r
9793   cp->hTo = hChildStdinWrDup;\r
9794 \r
9795   *pr = (void *) cp;\r
9796 \r
9797   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9798      2000 where engines sometimes don't see the initial command(s)\r
9799      from WinBoard and hang.  I don't understand how that can happen,\r
9800      but the Sleep is harmless, so I've put it in.  Others have also\r
9801      reported what may be the same problem, so hopefully this will fix\r
9802      it for them too.  */\r
9803   Sleep(500);\r
9804 \r
9805   return NO_ERROR;\r
9806 }\r
9807 \r
9808 \r
9809 void\r
9810 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9811 {\r
9812   ChildProc *cp; int result;\r
9813 \r
9814   cp = (ChildProc *) pr;\r
9815   if (cp == NULL) return;\r
9816 \r
9817   switch (cp->kind) {\r
9818   case CPReal:\r
9819     /* TerminateProcess is considered harmful, so... */\r
9820     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9821     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9822     /* The following doesn't work because the chess program\r
9823        doesn't "have the same console" as WinBoard.  Maybe\r
9824        we could arrange for this even though neither WinBoard\r
9825        nor the chess program uses a console for stdio? */\r
9826     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9827 \r
9828     /* [AS] Special termination modes for misbehaving programs... */\r
9829     if( signal == 9 ) { \r
9830         result = TerminateProcess( cp->hProcess, 0 );\r
9831 \r
9832         if ( appData.debugMode) {\r
9833             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9834         }\r
9835     }\r
9836     else if( signal == 10 ) {\r
9837         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9838 \r
9839         if( dw != WAIT_OBJECT_0 ) {\r
9840             result = TerminateProcess( cp->hProcess, 0 );\r
9841 \r
9842             if ( appData.debugMode) {\r
9843                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9844             }\r
9845 \r
9846         }\r
9847     }\r
9848 \r
9849     CloseHandle(cp->hProcess);\r
9850     break;\r
9851 \r
9852   case CPComm:\r
9853     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9854     break;\r
9855 \r
9856   case CPSock:\r
9857     closesocket(cp->sock);\r
9858     WSACleanup();\r
9859     break;\r
9860 \r
9861   case CPRcmd:\r
9862     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9863     closesocket(cp->sock);\r
9864     closesocket(cp->sock2);\r
9865     WSACleanup();\r
9866     break;\r
9867   }\r
9868   free(cp);\r
9869 }\r
9870 \r
9871 void\r
9872 InterruptChildProcess(ProcRef pr)\r
9873 {\r
9874   ChildProc *cp;\r
9875 \r
9876   cp = (ChildProc *) pr;\r
9877   if (cp == NULL) return;\r
9878   switch (cp->kind) {\r
9879   case CPReal:\r
9880     /* The following doesn't work because the chess program\r
9881        doesn't "have the same console" as WinBoard.  Maybe\r
9882        we could arrange for this even though neither WinBoard\r
9883        nor the chess program uses a console for stdio */\r
9884     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9885     break;\r
9886 \r
9887   case CPComm:\r
9888   case CPSock:\r
9889     /* Can't interrupt */\r
9890     break;\r
9891 \r
9892   case CPRcmd:\r
9893     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9894     break;\r
9895   }\r
9896 }\r
9897 \r
9898 \r
9899 int\r
9900 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9901 {\r
9902   char cmdLine[MSG_SIZ];\r
9903 \r
9904   if (port[0] == NULLCHAR) {\r
9905     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9906   } else {\r
9907     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9908   }\r
9909   return StartChildProcess(cmdLine, "", pr);\r
9910 }\r
9911 \r
9912 \r
9913 /* Code to open TCP sockets */\r
9914 \r
9915 int\r
9916 OpenTCP(char *host, char *port, ProcRef *pr)\r
9917 {\r
9918   ChildProc *cp;\r
9919   int err;\r
9920   SOCKET s;\r
9921   struct sockaddr_in sa, mysa;\r
9922   struct hostent FAR *hp;\r
9923   unsigned short uport;\r
9924   WORD wVersionRequested;\r
9925   WSADATA wsaData;\r
9926 \r
9927   /* Initialize socket DLL */\r
9928   wVersionRequested = MAKEWORD(1, 1);\r
9929   err = WSAStartup(wVersionRequested, &wsaData);\r
9930   if (err != 0) return err;\r
9931 \r
9932   /* Make socket */\r
9933   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9934     err = WSAGetLastError();\r
9935     WSACleanup();\r
9936     return err;\r
9937   }\r
9938 \r
9939   /* Bind local address using (mostly) don't-care values.\r
9940    */\r
9941   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9942   mysa.sin_family = AF_INET;\r
9943   mysa.sin_addr.s_addr = INADDR_ANY;\r
9944   uport = (unsigned short) 0;\r
9945   mysa.sin_port = htons(uport);\r
9946   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9947       == SOCKET_ERROR) {\r
9948     err = WSAGetLastError();\r
9949     WSACleanup();\r
9950     return err;\r
9951   }\r
9952 \r
9953   /* Resolve remote host name */\r
9954   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9955   if (!(hp = gethostbyname(host))) {\r
9956     unsigned int b0, b1, b2, b3;\r
9957 \r
9958     err = WSAGetLastError();\r
9959 \r
9960     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9961       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9962       hp->h_addrtype = AF_INET;\r
9963       hp->h_length = 4;\r
9964       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9965       hp->h_addr_list[0] = (char *) malloc(4);\r
9966       hp->h_addr_list[0][0] = (char) b0;\r
9967       hp->h_addr_list[0][1] = (char) b1;\r
9968       hp->h_addr_list[0][2] = (char) b2;\r
9969       hp->h_addr_list[0][3] = (char) b3;\r
9970     } else {\r
9971       WSACleanup();\r
9972       return err;\r
9973     }\r
9974   }\r
9975   sa.sin_family = hp->h_addrtype;\r
9976   uport = (unsigned short) atoi(port);\r
9977   sa.sin_port = htons(uport);\r
9978   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9979 \r
9980   /* Make connection */\r
9981   if (connect(s, (struct sockaddr *) &sa,\r
9982               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9983     err = WSAGetLastError();\r
9984     WSACleanup();\r
9985     return err;\r
9986   }\r
9987 \r
9988   /* Prepare return value */\r
9989   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9990   cp->kind = CPSock;\r
9991   cp->sock = s;\r
9992   *pr = (ProcRef *) cp;\r
9993 \r
9994   return NO_ERROR;\r
9995 }\r
9996 \r
9997 int\r
9998 OpenCommPort(char *name, ProcRef *pr)\r
9999 {\r
10000   HANDLE h;\r
10001   COMMTIMEOUTS ct;\r
10002   ChildProc *cp;\r
10003   char fullname[MSG_SIZ];\r
10004 \r
10005   if (*name != '\\')\r
10006     sprintf(fullname, "\\\\.\\%s", name);\r
10007   else\r
10008     strcpy(fullname, name);\r
10009 \r
10010   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10011                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10012   if (h == (HANDLE) -1) {\r
10013     return GetLastError();\r
10014   }\r
10015   hCommPort = h;\r
10016 \r
10017   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10018 \r
10019   /* Accumulate characters until a 100ms pause, then parse */\r
10020   ct.ReadIntervalTimeout = 100;\r
10021   ct.ReadTotalTimeoutMultiplier = 0;\r
10022   ct.ReadTotalTimeoutConstant = 0;\r
10023   ct.WriteTotalTimeoutMultiplier = 0;\r
10024   ct.WriteTotalTimeoutConstant = 0;\r
10025   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10026 \r
10027   /* Prepare return value */\r
10028   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10029   cp->kind = CPComm;\r
10030   cp->hFrom = h;\r
10031   cp->hTo = h;\r
10032   *pr = (ProcRef *) cp;\r
10033 \r
10034   return NO_ERROR;\r
10035 }\r
10036 \r
10037 int\r
10038 OpenLoopback(ProcRef *pr)\r
10039 {\r
10040   DisplayFatalError("Not implemented", 0, 1);\r
10041   return NO_ERROR;\r
10042 }\r
10043 \r
10044 \r
10045 int\r
10046 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10047 {\r
10048   ChildProc *cp;\r
10049   int err;\r
10050   SOCKET s, s2, s3;\r
10051   struct sockaddr_in sa, mysa;\r
10052   struct hostent FAR *hp;\r
10053   unsigned short uport;\r
10054   WORD wVersionRequested;\r
10055   WSADATA wsaData;\r
10056   int fromPort;\r
10057   char stderrPortStr[MSG_SIZ];\r
10058 \r
10059   /* Initialize socket DLL */\r
10060   wVersionRequested = MAKEWORD(1, 1);\r
10061   err = WSAStartup(wVersionRequested, &wsaData);\r
10062   if (err != 0) return err;\r
10063 \r
10064   /* Resolve remote host name */\r
10065   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10066   if (!(hp = gethostbyname(host))) {\r
10067     unsigned int b0, b1, b2, b3;\r
10068 \r
10069     err = WSAGetLastError();\r
10070 \r
10071     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10072       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10073       hp->h_addrtype = AF_INET;\r
10074       hp->h_length = 4;\r
10075       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10076       hp->h_addr_list[0] = (char *) malloc(4);\r
10077       hp->h_addr_list[0][0] = (char) b0;\r
10078       hp->h_addr_list[0][1] = (char) b1;\r
10079       hp->h_addr_list[0][2] = (char) b2;\r
10080       hp->h_addr_list[0][3] = (char) b3;\r
10081     } else {\r
10082       WSACleanup();\r
10083       return err;\r
10084     }\r
10085   }\r
10086   sa.sin_family = hp->h_addrtype;\r
10087   uport = (unsigned short) 514;\r
10088   sa.sin_port = htons(uport);\r
10089   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10090 \r
10091   /* Bind local socket to unused "privileged" port address\r
10092    */\r
10093   s = INVALID_SOCKET;\r
10094   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10095   mysa.sin_family = AF_INET;\r
10096   mysa.sin_addr.s_addr = INADDR_ANY;\r
10097   for (fromPort = 1023;; fromPort--) {\r
10098     if (fromPort < 0) {\r
10099       WSACleanup();\r
10100       return WSAEADDRINUSE;\r
10101     }\r
10102     if (s == INVALID_SOCKET) {\r
10103       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10104         err = WSAGetLastError();\r
10105         WSACleanup();\r
10106         return err;\r
10107       }\r
10108     }\r
10109     uport = (unsigned short) fromPort;\r
10110     mysa.sin_port = htons(uport);\r
10111     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10112         == SOCKET_ERROR) {\r
10113       err = WSAGetLastError();\r
10114       if (err == WSAEADDRINUSE) continue;\r
10115       WSACleanup();\r
10116       return err;\r
10117     }\r
10118     if (connect(s, (struct sockaddr *) &sa,\r
10119       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10120       err = WSAGetLastError();\r
10121       if (err == WSAEADDRINUSE) {\r
10122         closesocket(s);\r
10123         s = -1;\r
10124         continue;\r
10125       }\r
10126       WSACleanup();\r
10127       return err;\r
10128     }\r
10129     break;\r
10130   }\r
10131 \r
10132   /* Bind stderr local socket to unused "privileged" port address\r
10133    */\r
10134   s2 = INVALID_SOCKET;\r
10135   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10136   mysa.sin_family = AF_INET;\r
10137   mysa.sin_addr.s_addr = INADDR_ANY;\r
10138   for (fromPort = 1023;; fromPort--) {\r
10139     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10140     if (fromPort < 0) {\r
10141       (void) closesocket(s);\r
10142       WSACleanup();\r
10143       return WSAEADDRINUSE;\r
10144     }\r
10145     if (s2 == INVALID_SOCKET) {\r
10146       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10147         err = WSAGetLastError();\r
10148         closesocket(s);\r
10149         WSACleanup();\r
10150         return err;\r
10151       }\r
10152     }\r
10153     uport = (unsigned short) fromPort;\r
10154     mysa.sin_port = htons(uport);\r
10155     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10156         == SOCKET_ERROR) {\r
10157       err = WSAGetLastError();\r
10158       if (err == WSAEADDRINUSE) continue;\r
10159       (void) closesocket(s);\r
10160       WSACleanup();\r
10161       return err;\r
10162     }\r
10163     if (listen(s2, 1) == SOCKET_ERROR) {\r
10164       err = WSAGetLastError();\r
10165       if (err == WSAEADDRINUSE) {\r
10166         closesocket(s2);\r
10167         s2 = INVALID_SOCKET;\r
10168         continue;\r
10169       }\r
10170       (void) closesocket(s);\r
10171       (void) closesocket(s2);\r
10172       WSACleanup();\r
10173       return err;\r
10174     }\r
10175     break;\r
10176   }\r
10177   prevStderrPort = fromPort; // remember port used\r
10178   sprintf(stderrPortStr, "%d", fromPort);\r
10179 \r
10180   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10181     err = WSAGetLastError();\r
10182     (void) closesocket(s);\r
10183     (void) closesocket(s2);\r
10184     WSACleanup();\r
10185     return err;\r
10186   }\r
10187 \r
10188   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10189     err = WSAGetLastError();\r
10190     (void) closesocket(s);\r
10191     (void) closesocket(s2);\r
10192     WSACleanup();\r
10193     return err;\r
10194   }\r
10195   if (*user == NULLCHAR) user = UserName();\r
10196   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10204     err = WSAGetLastError();\r
10205     (void) closesocket(s);\r
10206     (void) closesocket(s2);\r
10207     WSACleanup();\r
10208     return err;\r
10209   }\r
10210 \r
10211   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10212     err = WSAGetLastError();\r
10213     (void) closesocket(s);\r
10214     (void) closesocket(s2);\r
10215     WSACleanup();\r
10216     return err;\r
10217   }\r
10218   (void) closesocket(s2);  /* Stop listening */\r
10219 \r
10220   /* Prepare return value */\r
10221   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10222   cp->kind = CPRcmd;\r
10223   cp->sock = s;\r
10224   cp->sock2 = s3;\r
10225   *pr = (ProcRef *) cp;\r
10226 \r
10227   return NO_ERROR;\r
10228 }\r
10229 \r
10230 \r
10231 InputSourceRef\r
10232 AddInputSource(ProcRef pr, int lineByLine,\r
10233                InputCallback func, VOIDSTAR closure)\r
10234 {\r
10235   InputSource *is, *is2 = NULL;\r
10236   ChildProc *cp = (ChildProc *) pr;\r
10237 \r
10238   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10239   is->lineByLine = lineByLine;\r
10240   is->func = func;\r
10241   is->closure = closure;\r
10242   is->second = NULL;\r
10243   is->next = is->buf;\r
10244   if (pr == NoProc) {\r
10245     is->kind = CPReal;\r
10246     consoleInputSource = is;\r
10247   } else {\r
10248     is->kind = cp->kind;\r
10249     /* \r
10250         [AS] Try to avoid a race condition if the thread is given control too early:\r
10251         we create all threads suspended so that the is->hThread variable can be\r
10252         safely assigned, then let the threads start with ResumeThread.\r
10253     */\r
10254     switch (cp->kind) {\r
10255     case CPReal:\r
10256       is->hFile = cp->hFrom;\r
10257       cp->hFrom = NULL; /* now owned by InputThread */\r
10258       is->hThread =\r
10259         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10260                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10261       break;\r
10262 \r
10263     case CPComm:\r
10264       is->hFile = cp->hFrom;\r
10265       cp->hFrom = NULL; /* now owned by InputThread */\r
10266       is->hThread =\r
10267         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10268                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10269       break;\r
10270 \r
10271     case CPSock:\r
10272       is->sock = cp->sock;\r
10273       is->hThread =\r
10274         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10275                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10276       break;\r
10277 \r
10278     case CPRcmd:\r
10279       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10280       *is2 = *is;\r
10281       is->sock = cp->sock;\r
10282       is->second = is2;\r
10283       is2->sock = cp->sock2;\r
10284       is2->second = is2;\r
10285       is->hThread =\r
10286         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10287                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10288       is2->hThread =\r
10289         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10290                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10291       break;\r
10292     }\r
10293 \r
10294     if( is->hThread != NULL ) {\r
10295         ResumeThread( is->hThread );\r
10296     }\r
10297 \r
10298     if( is2 != NULL && is2->hThread != NULL ) {\r
10299         ResumeThread( is2->hThread );\r
10300     }\r
10301   }\r
10302 \r
10303   return (InputSourceRef) is;\r
10304 }\r
10305 \r
10306 void\r
10307 RemoveInputSource(InputSourceRef isr)\r
10308 {\r
10309   InputSource *is;\r
10310 \r
10311   is = (InputSource *) isr;\r
10312   is->hThread = NULL;  /* tell thread to stop */\r
10313   CloseHandle(is->hThread);\r
10314   if (is->second != NULL) {\r
10315     is->second->hThread = NULL;\r
10316     CloseHandle(is->second->hThread);\r
10317   }\r
10318 }\r
10319 \r
10320 \r
10321 int\r
10322 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10323 {\r
10324   DWORD dOutCount;\r
10325   int outCount = SOCKET_ERROR;\r
10326   ChildProc *cp = (ChildProc *) pr;\r
10327   static OVERLAPPED ovl;\r
10328 \r
10329   if (pr == NoProc) {\r
10330     ConsoleOutput(message, count, FALSE);\r
10331     return count;\r
10332   } \r
10333 \r
10334   if (ovl.hEvent == NULL) {\r
10335     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10336   }\r
10337   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10338 \r
10339   switch (cp->kind) {\r
10340   case CPSock:\r
10341   case CPRcmd:\r
10342     outCount = send(cp->sock, message, count, 0);\r
10343     if (outCount == SOCKET_ERROR) {\r
10344       *outError = WSAGetLastError();\r
10345     } else {\r
10346       *outError = NO_ERROR;\r
10347     }\r
10348     break;\r
10349 \r
10350   case CPReal:\r
10351     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10352                   &dOutCount, NULL)) {\r
10353       *outError = NO_ERROR;\r
10354       outCount = (int) dOutCount;\r
10355     } else {\r
10356       *outError = GetLastError();\r
10357     }\r
10358     break;\r
10359 \r
10360   case CPComm:\r
10361     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10362                             &dOutCount, &ovl);\r
10363     if (*outError == NO_ERROR) {\r
10364       outCount = (int) dOutCount;\r
10365     }\r
10366     break;\r
10367   }\r
10368   return outCount;\r
10369 }\r
10370 \r
10371 int\r
10372 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10373                        long msdelay)\r
10374 {\r
10375   /* Ignore delay, not implemented for WinBoard */\r
10376   return OutputToProcess(pr, message, count, outError);\r
10377 }\r
10378 \r
10379 \r
10380 void\r
10381 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10382                         char *buf, int count, int error)\r
10383 {\r
10384   DisplayFatalError("Not implemented", 0, 1);\r
10385 }\r
10386 \r
10387 /* see wgamelist.c for Game List functions */\r
10388 /* see wedittags.c for Edit Tags functions */\r
10389 \r
10390 \r
10391 VOID\r
10392 ICSInitScript()\r
10393 {\r
10394   FILE *f;\r
10395   char buf[MSG_SIZ];\r
10396   char *dummy;\r
10397 \r
10398   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10399     f = fopen(buf, "r");\r
10400     if (f != NULL) {\r
10401       ProcessICSInitScript(f);\r
10402       fclose(f);\r
10403     }\r
10404   }\r
10405 }\r
10406 \r
10407 \r
10408 VOID\r
10409 StartAnalysisClock()\r
10410 {\r
10411   if (analysisTimerEvent) return;\r
10412   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10413                                         (UINT) 2000, NULL);\r
10414 }\r
10415 \r
10416 LRESULT CALLBACK\r
10417 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10418 {\r
10419   static HANDLE hwndText;\r
10420   RECT rect;\r
10421   static int sizeX, sizeY;\r
10422   int newSizeX, newSizeY, flags;\r
10423   MINMAXINFO *mmi;\r
10424 \r
10425   switch (message) {\r
10426   case WM_INITDIALOG: /* message: initialize dialog box */\r
10427     /* Initialize the dialog items */\r
10428     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10429     SetWindowText(hDlg, analysisTitle);\r
10430     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10431     /* Size and position the dialog */\r
10432     if (!analysisDialog) {\r
10433       analysisDialog = hDlg;\r
10434       flags = SWP_NOZORDER;\r
10435       GetClientRect(hDlg, &rect);\r
10436       sizeX = rect.right;\r
10437       sizeY = rect.bottom;\r
10438       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10439           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10440         WINDOWPLACEMENT wp;\r
10441         EnsureOnScreen(&analysisX, &analysisY);\r
10442         wp.length = sizeof(WINDOWPLACEMENT);\r
10443         wp.flags = 0;\r
10444         wp.showCmd = SW_SHOW;\r
10445         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10446         wp.rcNormalPosition.left = analysisX;\r
10447         wp.rcNormalPosition.right = analysisX + analysisW;\r
10448         wp.rcNormalPosition.top = analysisY;\r
10449         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10450         SetWindowPlacement(hDlg, &wp);\r
10451 \r
10452         GetClientRect(hDlg, &rect);\r
10453         newSizeX = rect.right;\r
10454         newSizeY = rect.bottom;\r
10455         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10456                               newSizeX, newSizeY);\r
10457         sizeX = newSizeX;\r
10458         sizeY = newSizeY;\r
10459       }\r
10460     }\r
10461     return FALSE;\r
10462 \r
10463   case WM_COMMAND: /* message: received a command */\r
10464     switch (LOWORD(wParam)) {\r
10465     case IDCANCEL:\r
10466       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10467           ExitAnalyzeMode();\r
10468           ModeHighlight();\r
10469           return TRUE;\r
10470       }\r
10471       EditGameEvent();\r
10472       return TRUE;\r
10473     default:\r
10474       break;\r
10475     }\r
10476     break;\r
10477 \r
10478   case WM_SIZE:\r
10479     newSizeX = LOWORD(lParam);\r
10480     newSizeY = HIWORD(lParam);\r
10481     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10482     sizeX = newSizeX;\r
10483     sizeY = newSizeY;\r
10484     break;\r
10485 \r
10486   case WM_GETMINMAXINFO:\r
10487     /* Prevent resizing window too small */\r
10488     mmi = (MINMAXINFO *) lParam;\r
10489     mmi->ptMinTrackSize.x = 100;\r
10490     mmi->ptMinTrackSize.y = 100;\r
10491     break;\r
10492   }\r
10493   return FALSE;\r
10494 }\r
10495 \r
10496 VOID\r
10497 AnalysisPopUp(char* title, char* str)\r
10498 {\r
10499   FARPROC lpProc;\r
10500   char *p, *q;\r
10501 \r
10502   /* [AS] */\r
10503   EngineOutputPopUp();\r
10504   return;\r
10505 \r
10506   if (str == NULL) str = "";\r
10507   p = (char *) malloc(2 * strlen(str) + 2);\r
10508   q = p;\r
10509   while (*str) {\r
10510     if (*str == '\n') *q++ = '\r';\r
10511     *q++ = *str++;\r
10512   }\r
10513   *q = NULLCHAR;\r
10514   if (analysisText != NULL) free(analysisText);\r
10515   analysisText = p;\r
10516 \r
10517   if (analysisDialog) {\r
10518     SetWindowText(analysisDialog, title);\r
10519     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10520     ShowWindow(analysisDialog, SW_SHOW);\r
10521   } else {\r
10522     analysisTitle = title;\r
10523     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10524     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10525                  hwndMain, (DLGPROC)lpProc);\r
10526     FreeProcInstance(lpProc);\r
10527   }\r
10528   analysisDialogUp = TRUE;  \r
10529 }\r
10530 \r
10531 VOID\r
10532 AnalysisPopDown()\r
10533 {\r
10534   if (analysisDialog) {\r
10535     ShowWindow(analysisDialog, SW_HIDE);\r
10536   }\r
10537   analysisDialogUp = FALSE;  \r
10538 }\r
10539 \r
10540 \r
10541 VOID\r
10542 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10543 {\r
10544   highlightInfo.sq[0].x = fromX;\r
10545   highlightInfo.sq[0].y = fromY;\r
10546   highlightInfo.sq[1].x = toX;\r
10547   highlightInfo.sq[1].y = toY;\r
10548 }\r
10549 \r
10550 VOID\r
10551 ClearHighlights()\r
10552 {\r
10553   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10554     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10555 }\r
10556 \r
10557 VOID\r
10558 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10559 {\r
10560   premoveHighlightInfo.sq[0].x = fromX;\r
10561   premoveHighlightInfo.sq[0].y = fromY;\r
10562   premoveHighlightInfo.sq[1].x = toX;\r
10563   premoveHighlightInfo.sq[1].y = toY;\r
10564 }\r
10565 \r
10566 VOID\r
10567 ClearPremoveHighlights()\r
10568 {\r
10569   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10570     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10571 }\r
10572 \r
10573 VOID\r
10574 ShutDownFrontEnd()\r
10575 {\r
10576   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10577   DeleteClipboardTempFiles();\r
10578 }\r
10579 \r
10580 void\r
10581 BoardToTop()\r
10582 {\r
10583     if (IsIconic(hwndMain))\r
10584       ShowWindow(hwndMain, SW_RESTORE);\r
10585 \r
10586     SetActiveWindow(hwndMain);\r
10587 }\r
10588 \r
10589 /*\r
10590  * Prototypes for animation support routines\r
10591  */\r
10592 static void ScreenSquare(int column, int row, POINT * pt);\r
10593 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10594      POINT frames[], int * nFrames);\r
10595 \r
10596 \r
10597 void\r
10598 AnimateAtomicCapture(int toX, int toY, int nFrames)\r
10599 {       // [HGM] atomic: animate blast wave\r
10600         int i;\r
10601 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10602         explodeInfo.x = toX;\r
10603         explodeInfo.y = toY;\r
10604         for(i=0; i<nFrames; i++) {\r
10605             explodeInfo.radius = (i*180)/(nFrames-1);\r
10606             DrawPosition(FALSE, NULL);\r
10607             Sleep(appData.animSpeed);\r
10608         }\r
10609         explodeInfo.radius = 0;\r
10610         DrawPosition(TRUE, NULL);\r
10611 }\r
10612 \r
10613 #define kFactor 4\r
10614 \r
10615 void\r
10616 AnimateMove(board, fromX, fromY, toX, toY)\r
10617      Board board;\r
10618      int fromX;\r
10619      int fromY;\r
10620      int toX;\r
10621      int toY;\r
10622 {\r
10623   ChessSquare piece;\r
10624   POINT start, finish, mid;\r
10625   POINT frames[kFactor * 2 + 1];\r
10626   int nFrames, n;\r
10627 \r
10628   if (!appData.animate) return;\r
10629   if (doingSizing) return;\r
10630   if (fromY < 0 || fromX < 0) return;\r
10631   piece = board[fromY][fromX];\r
10632   if (piece >= EmptySquare) return;\r
10633 \r
10634   ScreenSquare(fromX, fromY, &start);\r
10635   ScreenSquare(toX, toY, &finish);\r
10636 \r
10637   /* All pieces except knights move in straight line */\r
10638   if (piece != WhiteKnight && piece != BlackKnight) {\r
10639     mid.x = start.x + (finish.x - start.x) / 2;\r
10640     mid.y = start.y + (finish.y - start.y) / 2;\r
10641   } else {\r
10642     /* Knight: make diagonal movement then straight */\r
10643     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10644        mid.x = start.x + (finish.x - start.x) / 2;\r
10645        mid.y = finish.y;\r
10646      } else {\r
10647        mid.x = finish.x;\r
10648        mid.y = start.y + (finish.y - start.y) / 2;\r
10649      }\r
10650   }\r
10651   \r
10652   /* Don't use as many frames for very short moves */\r
10653   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10654     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10655   else\r
10656     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10657 \r
10658   animInfo.from.x = fromX;\r
10659   animInfo.from.y = fromY;\r
10660   animInfo.to.x = toX;\r
10661   animInfo.to.y = toY;\r
10662   animInfo.lastpos = start;\r
10663   animInfo.piece = piece;\r
10664   for (n = 0; n < nFrames; n++) {\r
10665     animInfo.pos = frames[n];\r
10666     DrawPosition(FALSE, NULL);\r
10667     animInfo.lastpos = animInfo.pos;\r
10668     Sleep(appData.animSpeed);\r
10669   }\r
10670   animInfo.pos = finish;\r
10671   DrawPosition(FALSE, NULL);\r
10672   animInfo.piece = EmptySquare;\r
10673   if(gameInfo.variant == VariantAtomic && board[toY][toX] != EmptySquare)\r
10674     AnimateAtomicCapture(toX, toY, 2*nFrames);\r
10675 }\r
10676 \r
10677 /*      Convert board position to corner of screen rect and color       */\r
10678 \r
10679 static void\r
10680 ScreenSquare(column, row, pt)\r
10681      int column; int row; POINT * pt;\r
10682 {\r
10683   if (flipView) {\r
10684     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10685     pt->y = lineGap + row * (squareSize + lineGap);\r
10686   } else {\r
10687     pt->x = lineGap + column * (squareSize + lineGap);\r
10688     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10689   }\r
10690 }\r
10691 \r
10692 /*      Generate a series of frame coords from start->mid->finish.\r
10693         The movement rate doubles until the half way point is\r
10694         reached, then halves back down to the final destination,\r
10695         which gives a nice slow in/out effect. The algorithmn\r
10696         may seem to generate too many intermediates for short\r
10697         moves, but remember that the purpose is to attract the\r
10698         viewers attention to the piece about to be moved and\r
10699         then to where it ends up. Too few frames would be less\r
10700         noticeable.                                             */\r
10701 \r
10702 static void\r
10703 Tween(start, mid, finish, factor, frames, nFrames)\r
10704      POINT * start; POINT * mid;\r
10705      POINT * finish; int factor;\r
10706      POINT frames[]; int * nFrames;\r
10707 {\r
10708   int n, fraction = 1, count = 0;\r
10709 \r
10710   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10711   for (n = 0; n < factor; n++)\r
10712     fraction *= 2;\r
10713   for (n = 0; n < factor; n++) {\r
10714     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10715     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10716     count ++;\r
10717     fraction = fraction / 2;\r
10718   }\r
10719   \r
10720   /* Midpoint */\r
10721   frames[count] = *mid;\r
10722   count ++;\r
10723   \r
10724   /* Slow out, stepping 1/2, then 1/4, ... */\r
10725   fraction = 2;\r
10726   for (n = 0; n < factor; n++) {\r
10727     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10728     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10729     count ++;\r
10730     fraction = fraction * 2;\r
10731   }\r
10732   *nFrames = count;\r
10733 }\r
10734 \r
10735 void\r
10736 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10737 {\r
10738 #if 0\r
10739     char buf[256];\r
10740 \r
10741     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10742         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10743 \r
10744     OutputDebugString( buf );\r
10745 #endif\r
10746 \r
10747     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10748 \r
10749     EvalGraphSet( first, last, current, pvInfoList );\r
10750 }\r
10751 \r
10752 void SetProgramStats( FrontEndProgramStats * stats )\r
10753 {\r
10754 #if 0\r
10755     char buf[1024];\r
10756 \r
10757     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10758         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10759 \r
10760     OutputDebugString( buf );\r
10761 #endif\r
10762 \r
10763     EngineOutputUpdate( stats );\r
10764 }\r