changes from H.G. Muller; version 4.3.16
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
144 static int logoHeight = 0;\r
145 static char messageText[MESSAGE_TEXT_MAX];\r
146 static int clockTimerEvent = 0;\r
147 static int loadGameTimerEvent = 0;\r
148 static int analysisTimerEvent = 0;\r
149 static DelayedEventCallback delayedTimerCallback;\r
150 static int delayedTimerEvent = 0;\r
151 static int buttonCount = 2;\r
152 char *icsTextMenuString;\r
153 char *icsNames;\r
154 char *firstChessProgramNames;\r
155 char *secondChessProgramNames;\r
156 \r
157 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
158 \r
159 #define PALETTESIZE 256\r
160 \r
161 HINSTANCE hInst;          /* current instance */\r
162 HWND hwndMain = NULL;        /* root window*/\r
163 HWND hwndConsole = NULL;\r
164 BOOLEAN alwaysOnTop = FALSE;\r
165 RECT boardRect;\r
166 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
167   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
168 HPALETTE hPal;\r
169 ColorClass currentColorClass;\r
170 \r
171 HWND hCommPort = NULL;    /* currently open comm port */\r
172 static HWND hwndPause;    /* pause button */\r
173 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
174 static HBRUSH lightSquareBrush, darkSquareBrush,\r
175   blackSquareBrush, /* [HGM] for band between board and holdings */\r
176   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
177 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
178 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
179 static HPEN gridPen = NULL;\r
180 static HPEN highlightPen = NULL;\r
181 static HPEN premovePen = NULL;\r
182 static NPLOGPALETTE pLogPal;\r
183 static BOOL paletteChanged = FALSE;\r
184 static HICON iconWhite, iconBlack, iconCurrent;\r
185 static int doingSizing = FALSE;\r
186 static int lastSizing = 0;\r
187 static int prevStderrPort;\r
188 \r
189 /* [AS] Support for background textures */\r
190 #define BACK_TEXTURE_MODE_DISABLED      0\r
191 #define BACK_TEXTURE_MODE_PLAIN         1\r
192 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
193 \r
194 static HBITMAP liteBackTexture = NULL;\r
195 static HBITMAP darkBackTexture = NULL;\r
196 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
198 static int backTextureSquareSize = 0;\r
199 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
200 \r
201 #if __GNUC__ && !defined(_winmajor)\r
202 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
203 #else\r
204 #define oldDialog (_winmajor < 4)\r
205 #endif\r
206 \r
207 char *defaultTextAttribs[] = \r
208 {\r
209   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
210   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
211   COLOR_NONE\r
212 };\r
213 \r
214 typedef struct {\r
215   char *name;\r
216   int squareSize;\r
217   int lineGap;\r
218   int smallLayout;\r
219   int tinyLayout;\r
220   int cliWidth, cliHeight;\r
221 } SizeInfo;\r
222 \r
223 SizeInfo sizeInfo[] = \r
224 {\r
225   { "tiny",     21, 0, 1, 1, 0, 0 },\r
226   { "teeny",    25, 1, 1, 1, 0, 0 },\r
227   { "dinky",    29, 1, 1, 1, 0, 0 },\r
228   { "petite",   33, 1, 1, 1, 0, 0 },\r
229   { "slim",     37, 2, 1, 0, 0, 0 },\r
230   { "small",    40, 2, 1, 0, 0, 0 },\r
231   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
232   { "middling", 49, 2, 0, 0, 0, 0 },\r
233   { "average",  54, 2, 0, 0, 0, 0 },\r
234   { "moderate", 58, 3, 0, 0, 0, 0 },\r
235   { "medium",   64, 3, 0, 0, 0, 0 },\r
236   { "bulky",    72, 3, 0, 0, 0, 0 },\r
237   { "large",    80, 3, 0, 0, 0, 0 },\r
238   { "big",      87, 3, 0, 0, 0, 0 },\r
239   { "huge",     95, 3, 0, 0, 0, 0 },\r
240   { "giant",    108, 3, 0, 0, 0, 0 },\r
241   { "colossal", 116, 4, 0, 0, 0, 0 },\r
242   { "titanic",  129, 4, 0, 0, 0, 0 },\r
243   { NULL, 0, 0, 0, 0, 0, 0 }\r
244 };\r
245 \r
246 #define MF(x) {x, {0, }, {0, }, 0}\r
247 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
248 {\r
249   { 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
250   { 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
251   { 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
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267 };\r
268 \r
269 MyFont *font[NUM_SIZES][NUM_FONTS];\r
270 \r
271 typedef struct {\r
272   char *label;\r
273   int id;\r
274   HWND hwnd;\r
275   WNDPROC wndproc;\r
276 } MyButtonDesc;\r
277 \r
278 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
279 #define N_BUTTONS 5\r
280 \r
281 MyButtonDesc buttonDesc[N_BUTTONS] =\r
282 {\r
283   {"<<", IDM_ToStart, NULL, NULL},\r
284   {"<", IDM_Backward, NULL, NULL},\r
285   {"P", IDM_Pause, NULL, NULL},\r
286   {">", IDM_Forward, NULL, NULL},\r
287   {">>", IDM_ToEnd, NULL, NULL},\r
288 };\r
289 \r
290 int tinyLayout = 0, smallLayout = 0;\r
291 #define MENU_BAR_ITEMS 6\r
292 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
293   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
294   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
295 };\r
296 \r
297 \r
298 MySound sounds[(int)NSoundClasses];\r
299 MyTextAttribs textAttribs[(int)NColorClasses];\r
300 \r
301 MyColorizeAttribs colorizeAttribs[] = {\r
302   { (COLORREF)0, 0, "Shout Text" },\r
303   { (COLORREF)0, 0, "SShout/CShout" },\r
304   { (COLORREF)0, 0, "Channel 1 Text" },\r
305   { (COLORREF)0, 0, "Channel Text" },\r
306   { (COLORREF)0, 0, "Kibitz Text" },\r
307   { (COLORREF)0, 0, "Tell Text" },\r
308   { (COLORREF)0, 0, "Challenge Text" },\r
309   { (COLORREF)0, 0, "Request Text" },\r
310   { (COLORREF)0, 0, "Seek Text" },\r
311   { (COLORREF)0, 0, "Normal Text" },\r
312   { (COLORREF)0, 0, "None" }\r
313 };\r
314 \r
315 \r
316 \r
317 static char *commentTitle;\r
318 static char *commentText;\r
319 static int commentIndex;\r
320 static Boolean editComment = FALSE;\r
321 HWND commentDialog = NULL;\r
322 BOOLEAN commentDialogUp = FALSE;\r
323 static int commentX, commentY, commentH, commentW;\r
324 \r
325 static char *analysisTitle;\r
326 static char *analysisText;\r
327 HWND analysisDialog = NULL;\r
328 BOOLEAN analysisDialogUp = FALSE;\r
329 static int analysisX, analysisY, analysisH, analysisW;\r
330 \r
331 char errorTitle[MSG_SIZ];\r
332 char errorMessage[2*MSG_SIZ];\r
333 HWND errorDialog = NULL;\r
334 BOOLEAN moveErrorMessageUp = FALSE;\r
335 BOOLEAN consoleEcho = TRUE;\r
336 CHARFORMAT consoleCF;\r
337 COLORREF consoleBackgroundColor;\r
338 \r
339 char *programVersion;\r
340 \r
341 #define CPReal 1\r
342 #define CPComm 2\r
343 #define CPSock 3\r
344 #define CPRcmd 4\r
345 typedef int CPKind;\r
346 \r
347 typedef struct {\r
348   CPKind kind;\r
349   HANDLE hProcess;\r
350   DWORD pid;\r
351   HANDLE hTo;\r
352   HANDLE hFrom;\r
353   SOCKET sock;\r
354   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
355 } ChildProc;\r
356 \r
357 #define INPUT_SOURCE_BUF_SIZE 4096\r
358 \r
359 typedef struct _InputSource {\r
360   CPKind kind;\r
361   HANDLE hFile;\r
362   SOCKET sock;\r
363   int lineByLine;\r
364   HANDLE hThread;\r
365   DWORD id;\r
366   char buf[INPUT_SOURCE_BUF_SIZE];\r
367   char *next;\r
368   DWORD count;\r
369   int error;\r
370   InputCallback func;\r
371   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
372   VOIDSTAR closure;\r
373 } InputSource;\r
374 \r
375 InputSource *consoleInputSource;\r
376 \r
377 DCB dcb;\r
378 \r
379 /* forward */\r
380 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
381 VOID ConsoleCreate();\r
382 LRESULT CALLBACK\r
383   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
384 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
385 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
386 VOID ParseCommSettings(char *arg, DCB *dcb);\r
387 LRESULT CALLBACK\r
388   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
389 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
390 void ParseIcsTextMenu(char *icsTextMenuString);\r
391 VOID PopUpMoveDialog(char firstchar);\r
392 VOID PopUpNameDialog(char firstchar);\r
393 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
394 \r
395 /* [AS] */\r
396 int NewGameFRC();\r
397 int GameListOptions();\r
398 \r
399 HWND moveHistoryDialog = NULL;\r
400 BOOLEAN moveHistoryDialogUp = FALSE;\r
401 \r
402 WindowPlacement wpMoveHistory;\r
403 \r
404 HWND evalGraphDialog = NULL;\r
405 BOOLEAN evalGraphDialogUp = FALSE;\r
406 \r
407 WindowPlacement wpEvalGraph;\r
408 \r
409 HWND engineOutputDialog = NULL;\r
410 BOOLEAN engineOutputDialogUp = FALSE;\r
411 \r
412 WindowPlacement wpEngineOutput;\r
413 \r
414 VOID MoveHistoryPopUp();\r
415 VOID MoveHistoryPopDown();\r
416 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
417 BOOL MoveHistoryIsUp();\r
418 \r
419 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
420 VOID EvalGraphPopUp();\r
421 VOID EvalGraphPopDown();\r
422 BOOL EvalGraphIsUp();\r
423 \r
424 VOID EngineOutputPopUp();\r
425 VOID EngineOutputPopDown();\r
426 BOOL EngineOutputIsUp();\r
427 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
428 \r
429 VOID GothicPopUp(char *title, VariantClass variant);\r
430 /*\r
431  * Setting "frozen" should disable all user input other than deleting\r
432  * the window.  We do this while engines are initializing themselves.\r
433  */\r
434 static int frozen = 0;\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];\r
436 void FreezeUI()\r
437 {\r
438   HMENU hmenu;\r
439   int i;\r
440 \r
441   if (frozen) return;\r
442   frozen = 1;\r
443   hmenu = GetMenu(hwndMain);\r
444   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
445     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
446   }\r
447   DrawMenuBar(hwndMain);\r
448 }\r
449 \r
450 /* Undo a FreezeUI */\r
451 void ThawUI()\r
452 {\r
453   HMENU hmenu;\r
454   int i;\r
455 \r
456   if (!frozen) return;\r
457   frozen = 0;\r
458   hmenu = GetMenu(hwndMain);\r
459   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
460     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
461   }\r
462   DrawMenuBar(hwndMain);\r
463 }\r
464 \r
465 /*---------------------------------------------------------------------------*\\r
466  *\r
467  * WinMain\r
468  *\r
469 \*---------------------------------------------------------------------------*/\r
470 \r
471 int APIENTRY\r
472 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
473         LPSTR lpCmdLine, int nCmdShow)\r
474 {\r
475   MSG msg;\r
476   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
477 \r
478   debugFP = stderr;\r
479 \r
480   LoadLibrary("RICHED32.DLL");\r
481   consoleCF.cbSize = sizeof(CHARFORMAT);\r
482 \r
483   if (!InitApplication(hInstance)) {\r
484     return (FALSE);\r
485   }\r
486   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
487     return (FALSE);\r
488   }\r
489 \r
490   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
491   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
492   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
493 \r
494   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
495 \r
496   while (GetMessage(&msg, /* message structure */\r
497                     NULL, /* handle of window receiving the message */\r
498                     0,    /* lowest message to examine */\r
499                     0))   /* highest message to examine */\r
500     {\r
501       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
502           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
503           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
504           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
505           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
506           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
507           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
508           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
509           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
510           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
511         TranslateMessage(&msg); /* Translates virtual key codes */\r
512         DispatchMessage(&msg);  /* Dispatches message to window */\r
513       }\r
514     }\r
515 \r
516 \r
517   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
518 }\r
519 \r
520 /*---------------------------------------------------------------------------*\\r
521  *\r
522  * Initialization functions\r
523  *\r
524 \*---------------------------------------------------------------------------*/\r
525 \r
526 BOOL\r
527 InitApplication(HINSTANCE hInstance)\r
528 {\r
529   WNDCLASS wc;\r
530 \r
531   /* Fill in window class structure with parameters that describe the */\r
532   /* main window. */\r
533 \r
534   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
535   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
536   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
537   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
538   wc.hInstance     = hInstance;         /* Owner of this class */\r
539   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
540   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
541   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
542   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
543   wc.lpszClassName = szAppName;                 /* Name to register as */\r
544 \r
545   /* Register the window class and return success/failure code. */\r
546   if (!RegisterClass(&wc)) return FALSE;\r
547 \r
548   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
549   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
550   wc.cbClsExtra    = 0;\r
551   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
552   wc.hInstance     = hInstance;\r
553   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
554   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
555   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
556   wc.lpszMenuName  = NULL;\r
557   wc.lpszClassName = szConsoleName;\r
558 \r
559   if (!RegisterClass(&wc)) return FALSE;\r
560   return TRUE;\r
561 }\r
562 \r
563 \r
564 /* Set by InitInstance, used by EnsureOnScreen */\r
565 int screenHeight, screenWidth;\r
566 \r
567 void\r
568 EnsureOnScreen(int *x, int *y)\r
569 {\r
570   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
571   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
572   if (*x > screenWidth - 32) *x = 0;\r
573   if (*y > screenHeight - 32) *y = 0;\r
574   if (*x < 10) *x = 10;\r
575   if (*y < gap) *y = gap;\r
576 }\r
577 \r
578 BOOL\r
579 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
580 {\r
581   HWND hwnd; /* Main window handle. */\r
582   int ibs;\r
583   WINDOWPLACEMENT wp;\r
584   char *filepart;\r
585 \r
586   hInst = hInstance;    /* Store instance handle in our global variable */\r
587 \r
588   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
589     *filepart = NULLCHAR;\r
590   } else {\r
591     GetCurrentDirectory(MSG_SIZ, installDir);\r
592   }\r
593   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
594   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
595   if (appData.debugMode) {\r
596     debugFP = fopen(appData.nameOfDebugFile, "w");\r
597     setbuf(debugFP, NULL);\r
598   }\r
599 \r
600   InitBackEnd1();\r
601 \r
602 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
603 //  InitEngineUCI( installDir, &second );\r
604 \r
605   /* Create a main window for this application instance. */\r
606   hwnd = CreateWindow(szAppName, szTitle,\r
607                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
608                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
609                       NULL, NULL, hInstance, NULL);\r
610   hwndMain = hwnd;\r
611 \r
612   /* If window could not be created, return "failure" */\r
613   if (!hwnd) {\r
614     return (FALSE);\r
615   }\r
616 \r
617   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
618   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
619       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
620 \r
621       if (first.programLogo == NULL && appData.debugMode) {\r
622           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
623       }\r
624   } else if(appData.autoLogo) {\r
625       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
626         char buf[MSG_SIZ];\r
627         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
628         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
629       }\r
630   }\r
631 \r
632   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
633       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
634 \r
635       if (second.programLogo == NULL && appData.debugMode) {\r
636           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
637       }\r
638   } else if(appData.autoLogo) {\r
639       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
640         char buf[MSG_SIZ];\r
641         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
642         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
643       }\r
644   }\r
645 \r
646   iconWhite = LoadIcon(hInstance, "icon_white");\r
647   iconBlack = LoadIcon(hInstance, "icon_black");\r
648   iconCurrent = iconWhite;\r
649   InitDrawingColors();\r
650   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
651   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
652   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
653     /* Compute window size for each board size, and use the largest\r
654        size that fits on this screen as the default. */\r
655     InitDrawingSizes((BoardSize)ibs, 0);\r
656     if (boardSize == (BoardSize)-1 &&\r
657         winHeight <= screenHeight\r
658            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
659         && winWidth <= screenWidth) {\r
660       boardSize = (BoardSize)ibs;\r
661     }\r
662   }\r
663 \r
664   InitDrawingSizes(boardSize, 0);\r
665   InitMenuChecks();\r
666   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
667 \r
668   /* [AS] Load textures if specified */\r
669   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
670   \r
671   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
672       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
673       liteBackTextureMode = appData.liteBackTextureMode;\r
674 \r
675       if (liteBackTexture == NULL && appData.debugMode) {\r
676           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
677       }\r
678   }\r
679   \r
680   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
681       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
682       darkBackTextureMode = appData.darkBackTextureMode;\r
683 \r
684       if (darkBackTexture == NULL && appData.debugMode) {\r
685           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
686       }\r
687   }\r
688 \r
689   mysrandom( (unsigned) time(NULL) );\r
690 \r
691   /* Make a console window if needed */\r
692   if (appData.icsActive) {\r
693     ConsoleCreate();\r
694   }\r
695 \r
696   /* [AS] Restore layout */\r
697   if( wpMoveHistory.visible ) {\r
698       MoveHistoryPopUp();\r
699   }\r
700 \r
701   if( wpEvalGraph.visible ) {\r
702       EvalGraphPopUp();\r
703   }\r
704 \r
705   if( wpEngineOutput.visible ) {\r
706       EngineOutputPopUp();\r
707   }\r
708 \r
709   InitBackEnd2();\r
710 \r
711   /* Make the window visible; update its client area; and return "success" */\r
712   EnsureOnScreen(&boardX, &boardY);\r
713   wp.length = sizeof(WINDOWPLACEMENT);\r
714   wp.flags = 0;\r
715   wp.showCmd = nCmdShow;\r
716   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
717   wp.rcNormalPosition.left = boardX;\r
718   wp.rcNormalPosition.right = boardX + winWidth;\r
719   wp.rcNormalPosition.top = boardY;\r
720   wp.rcNormalPosition.bottom = boardY + winHeight;\r
721   SetWindowPlacement(hwndMain, &wp);\r
722 \r
723   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
724                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
725 \r
726 #if 0\r
727   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
728   if( gameInfo.variant != VariantFischeRandom ) {\r
729       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
730   }\r
731 #endif\r
732   if (hwndConsole) {\r
733 #if AOT_CONSOLE\r
734     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
735                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
736 #endif\r
737     ShowWindow(hwndConsole, nCmdShow);\r
738   }\r
739   UpdateWindow(hwnd);\r
740 \r
741   return TRUE;\r
742 \r
743 }\r
744 \r
745 \r
746 typedef enum {\r
747   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
748   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
749   ArgSettingsFilename\r
750 } ArgType;\r
751 \r
752 typedef struct {\r
753   char *argName;\r
754   ArgType argType;\r
755   /***\r
756   union {\r
757     String *pString;       // ArgString\r
758     int *pInt;             // ArgInt\r
759     float *pFloat;         // ArgFloat\r
760     Boolean *pBoolean;     // ArgBoolean\r
761     COLORREF *pColor;      // ArgColor\r
762     ColorClass cc;         // ArgAttribs\r
763     String *pFilename;     // ArgFilename\r
764     BoardSize *pBoardSize; // ArgBoardSize\r
765     int whichFont;         // ArgFont\r
766     DCB *pDCB;             // ArgCommSettings\r
767     String *pFilename;     // ArgSettingsFilename\r
768   } argLoc;\r
769   ***/\r
770   LPVOID argLoc;\r
771   BOOL save;\r
772 } ArgDescriptor;\r
773 \r
774 int junk;\r
775 ArgDescriptor argDescriptors[] = {\r
776   /* positional arguments */\r
777   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
778   { "", ArgNone, NULL },\r
779   /* keyword arguments */\r
780   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
781   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
782   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
783   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
784   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
785   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
786   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
787   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
788   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
789   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
790   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
791   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
792   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
793   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
794   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
795   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
796   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
797   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
798     FALSE },\r
799   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
800     FALSE },\r
801   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
802     FALSE },\r
803   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
804   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
805     FALSE },\r
806   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
807   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
808   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
809   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
810   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
811   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
812   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
813   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
814   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
815   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
816   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
817   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
818   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
819   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
820   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
821   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
822   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
823   /*!!bitmapDirectory?*/\r
824   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
825   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
826   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
827   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
828   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
829   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
830   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
831   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
832   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
833   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
834   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
835   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
836   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
837   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
838   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
839   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
840   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
841   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
842   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
843   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
844   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
845   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
846   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
847   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
848   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
849   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
850   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
851   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
852   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
853   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
854   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
855   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
856   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
857   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
858   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
859   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
860   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
861   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
862   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
863   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
864   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
865   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
866   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
867   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
868   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
869   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
870   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
871   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
872   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
873   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
874   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
875   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
876   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
877   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
878   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
879   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
880   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
881   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
882   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
883   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
884   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
885   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
886   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
887   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
888   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
889   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
890   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
891   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
892   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
893   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
894   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
895   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
896   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
897   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
898   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
899   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
900   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
901   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
902   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
903   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
904   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
905   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
906   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
907   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
908   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
909   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
910   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
911   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
912   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
913   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
914   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
915   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
916   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
917   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
918     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
919   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
920   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
921   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
922   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
923   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
924   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
925   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
926   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
927     TRUE }, /* must come after all fonts */\r
928   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
929   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
930     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
931   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
932   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
933   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
934   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
935   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
936   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
937   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
938   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
939   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
940   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
941   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
942   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
943   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
944   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
945   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
946   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
947   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
948   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
949   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
950   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
951   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
952   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
953   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
954   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
955   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
956   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
957   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
958   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
959 #if 0\r
960   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
961   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
962 #endif\r
963   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
964   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
965   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
966   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
967   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
968   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
969   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
970   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
971   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
972   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
973   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
974   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
975   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
976   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
977   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
978   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
979   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
980   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
981   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
982   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
983   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
984   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
985   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
986   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
987   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
988   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
989   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
990   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
991   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
992   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
993   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
994   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
995   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
996   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
997   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
998   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
999   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1000   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1001   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1002   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1003   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1004   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1005   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1006   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1007   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1008   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1009   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1010   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1011   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1012   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1013   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1014   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1015   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1016   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1017   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1018   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1019   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1020   { "highlightLastMove", ArgBoolean,\r
1021     (LPVOID) &appData.highlightLastMove, TRUE },\r
1022   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1023   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1024   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1025   { "highlightDragging", ArgBoolean,\r
1026     (LPVOID) &appData.highlightDragging, TRUE },\r
1027   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1028   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1029   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1030   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1031   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1032   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1033   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1034   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1035   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1036   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1037   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1038   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1039   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1040   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1041   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1042   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1043   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1044   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1045   { "soundShout", ArgFilename,\r
1046     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1047   { "soundSShout", ArgFilename,\r
1048     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1049   { "soundChannel1", ArgFilename,\r
1050     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1051   { "soundChannel", ArgFilename,\r
1052     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1053   { "soundKibitz", ArgFilename,\r
1054     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1055   { "soundTell", ArgFilename,\r
1056     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1057   { "soundChallenge", ArgFilename,\r
1058     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1059   { "soundRequest", ArgFilename,\r
1060     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1061   { "soundSeek", ArgFilename,\r
1062     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1063   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1064   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1065   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1066   { "soundIcsLoss", ArgFilename, \r
1067     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1068   { "soundIcsDraw", ArgFilename, \r
1069     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1070   { "soundIcsUnfinished", ArgFilename, \r
1071     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1072   { "soundIcsAlarm", ArgFilename, \r
1073     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1074   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1075   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1076   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1077   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1078   { "reuseChessPrograms", ArgBoolean,\r
1079     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1080   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1081   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1082   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1083   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1084   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1085   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1086   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1087   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1088   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1089   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1090   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1091   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1092   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1093   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1094   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1095   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1096   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1097   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1098   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1099   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1100   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1101   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1102   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1103   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1104   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1105   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1106   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1107   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1108   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1109   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1110   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1111   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1112   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1113   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1114   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1115   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1116   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1117     TRUE },\r
1118   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1119     TRUE },\r
1120   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1121   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1122   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1123   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1124   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1125   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1126   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1127   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1128   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1129   /* [AS] New features */\r
1130   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1131   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1132   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1133   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1134   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1135   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1136   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1137   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1138   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1139   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1140   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1141   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1142   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1143   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1144   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1145   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1146   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1147   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1148   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1149   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1150   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1151   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1152   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1153   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1154   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1155   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1156   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1157   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1158   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1159   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1160   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1161   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1162   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1163   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1164   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1165   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1166   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1167   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1168   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1169   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1170   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1171   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1172   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1173   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1174   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1175   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1176   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1177   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1178 \r
1179   /* [AS] Layout stuff */\r
1180   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1181   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1182   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1183   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1184   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1185 \r
1186   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1187   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1188   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1189   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1190   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1191 \r
1192   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1193   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1194   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1195   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1196   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1197 \r
1198   /* [HGM] board-size, adjudication and misc. options */\r
1199   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1200   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1201   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1202   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1203   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1204   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1205   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1206   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1207   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1208   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1209   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1210   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1211   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1212   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1213   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1214   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1215   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1216   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1217   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1218   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1219   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1220   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1221   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1222   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1223   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1224   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1225   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1226 \r
1227 #ifdef ZIPPY\r
1228   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1229   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1230   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1231   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1232   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1233   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1234   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1235   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1236   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1237   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1238   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1239   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1240   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1241     FALSE },\r
1242   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1243   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1244   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1245   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1246   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1247   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1248   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1249     FALSE },\r
1250   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1251   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1252   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1253   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1254   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1255   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1256   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1257   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1258   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1259   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1260   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1261   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1262   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1263   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1264   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1265   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1266   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1267   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1268 #endif\r
1269   /* [HGM] options for broadcasting and time odds */\r
1270   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1271   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1272   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1273   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1274   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1275   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1276   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1277   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1278   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1279   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1280   { NULL, ArgNone, NULL, FALSE }\r
1281 };\r
1282 \r
1283 \r
1284 /* Kludge for indirection files on command line */\r
1285 char* lastIndirectionFilename;\r
1286 ArgDescriptor argDescriptorIndirection =\r
1287 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1288 \r
1289 \r
1290 VOID\r
1291 ExitArgError(char *msg, char *badArg)\r
1292 {\r
1293   char buf[MSG_SIZ];\r
1294 \r
1295   sprintf(buf, "%s %s", msg, badArg);\r
1296   DisplayFatalError(buf, 0, 2);\r
1297   exit(2);\r
1298 }\r
1299 \r
1300 /* Command line font name parser.  NULL name means do nothing.\r
1301    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1302    For backward compatibility, syntax without the colon is also\r
1303    accepted, but font names with digits in them won't work in that case.\r
1304 */\r
1305 VOID\r
1306 ParseFontName(char *name, MyFontParams *mfp)\r
1307 {\r
1308   char *p, *q;\r
1309   if (name == NULL) return;\r
1310   p = name;\r
1311   q = strchr(p, ':');\r
1312   if (q) {\r
1313     if (q - p >= sizeof(mfp->faceName))\r
1314       ExitArgError("Font name too long:", name);\r
1315     memcpy(mfp->faceName, p, q - p);\r
1316     mfp->faceName[q - p] = NULLCHAR;\r
1317     p = q + 1;\r
1318   } else {\r
1319     q = mfp->faceName;\r
1320     while (*p && !isdigit(*p)) {\r
1321       *q++ = *p++;\r
1322       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1323         ExitArgError("Font name too long:", name);\r
1324     }\r
1325     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1326     *q = NULLCHAR;\r
1327   }\r
1328   if (!*p) ExitArgError("Font point size missing:", name);\r
1329   mfp->pointSize = (float) atof(p);\r
1330   mfp->bold = (strchr(p, 'b') != NULL);\r
1331   mfp->italic = (strchr(p, 'i') != NULL);\r
1332   mfp->underline = (strchr(p, 'u') != NULL);\r
1333   mfp->strikeout = (strchr(p, 's') != NULL);\r
1334 }\r
1335 \r
1336 /* Color name parser.\r
1337    X version accepts X color names, but this one\r
1338    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1339 COLORREF\r
1340 ParseColorName(char *name)\r
1341 {\r
1342   int red, green, blue, count;\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1346   if (count != 3) {\r
1347     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1348       &red, &green, &blue);\r
1349   }\r
1350   if (count != 3) {\r
1351     sprintf(buf, "Can't parse color name %s", name);\r
1352     DisplayError(buf, 0);\r
1353     return RGB(0, 0, 0);\r
1354   }\r
1355   return PALETTERGB(red, green, blue);\r
1356 }\r
1357 \r
1358 \r
1359 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1360 {\r
1361   char *e = argValue;\r
1362   int eff = 0;\r
1363 \r
1364   while (*e) {\r
1365     if (*e == 'b')      eff |= CFE_BOLD;\r
1366     else if (*e == 'i') eff |= CFE_ITALIC;\r
1367     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1368     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1369     else if (*e == '#' || isdigit(*e)) break;\r
1370     e++;\r
1371   }\r
1372   *effects = eff;\r
1373   *color   = ParseColorName(e);\r
1374 }\r
1375 \r
1376 \r
1377 BoardSize\r
1378 ParseBoardSize(char *name)\r
1379 {\r
1380   BoardSize bs = SizeTiny;\r
1381   while (sizeInfo[bs].name != NULL) {\r
1382     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1383     bs++;\r
1384   }\r
1385   ExitArgError("Unrecognized board size value", name);\r
1386   return bs; /* not reached */\r
1387 }\r
1388 \r
1389 \r
1390 char\r
1391 StringGet(void *getClosure)\r
1392 {\r
1393   char **p = (char **) getClosure;\r
1394   return *((*p)++);\r
1395 }\r
1396 \r
1397 char\r
1398 FileGet(void *getClosure)\r
1399 {\r
1400   int c;\r
1401   FILE* f = (FILE*) getClosure;\r
1402 \r
1403   c = getc(f);\r
1404   if (c == EOF)\r
1405     return NULLCHAR;\r
1406   else\r
1407     return (char) c;\r
1408 }\r
1409 \r
1410 /* Parse settings file named "name". If file found, return the\r
1411    full name in fullname and return TRUE; else return FALSE */\r
1412 BOOLEAN\r
1413 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1414 {\r
1415   char *dummy;\r
1416   FILE *f;\r
1417 \r
1418   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1419     f = fopen(fullname, "r");\r
1420     if (f != NULL) {\r
1421       ParseArgs(FileGet, f);\r
1422       fclose(f);\r
1423       return TRUE;\r
1424     }\r
1425   }\r
1426   return FALSE;\r
1427 }\r
1428 \r
1429 VOID\r
1430 ParseArgs(GetFunc get, void *cl)\r
1431 {\r
1432   char argName[ARG_MAX];\r
1433   char argValue[ARG_MAX];\r
1434   ArgDescriptor *ad;\r
1435   char start;\r
1436   char *q;\r
1437   int i, octval;\r
1438   char ch;\r
1439   int posarg = 0;\r
1440 \r
1441   ch = get(cl);\r
1442   for (;;) {\r
1443     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1444     if (ch == NULLCHAR) break;\r
1445     if (ch == ';') {\r
1446       /* Comment to end of line */\r
1447       ch = get(cl);\r
1448       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1449       continue;\r
1450     } else if (ch == '/' || ch == '-') {\r
1451       /* Switch */\r
1452       q = argName;\r
1453       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1454              ch != '\n' && ch != '\t') {\r
1455         *q++ = ch;\r
1456         ch = get(cl);\r
1457       }\r
1458       *q = NULLCHAR;\r
1459 \r
1460       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1461         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1462 \r
1463       if (ad->argName == NULL)\r
1464         ExitArgError("Unrecognized argument", argName);\r
1465 \r
1466     } else if (ch == '@') {\r
1467       /* Indirection file */\r
1468       ad = &argDescriptorIndirection;\r
1469       ch = get(cl);\r
1470     } else {\r
1471       /* Positional argument */\r
1472       ad = &argDescriptors[posarg++];\r
1473       strcpy(argName, ad->argName);\r
1474     }\r
1475 \r
1476     if (ad->argType == ArgTrue) {\r
1477       *(Boolean *) ad->argLoc = TRUE;\r
1478       continue;\r
1479     }\r
1480     if (ad->argType == ArgFalse) {\r
1481       *(Boolean *) ad->argLoc = FALSE;\r
1482       continue;\r
1483     }\r
1484 \r
1485     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1486     if (ch == NULLCHAR || ch == '\n') {\r
1487       ExitArgError("No value provided for argument", argName);\r
1488     }\r
1489     q = argValue;\r
1490     if (ch == '{') {\r
1491       // Quoting with { }.  No characters have to (or can) be escaped.\r
1492       // Thus the string cannot contain a '}' character.\r
1493       start = ch;\r
1494       ch = get(cl);\r
1495       while (start) {\r
1496         switch (ch) {\r
1497         case NULLCHAR:\r
1498           start = NULLCHAR;\r
1499           break;\r
1500           \r
1501         case '}':\r
1502           ch = get(cl);\r
1503           start = NULLCHAR;\r
1504           break;\r
1505 \r
1506         default:\r
1507           *q++ = ch;\r
1508           ch = get(cl);\r
1509           break;\r
1510         }\r
1511       }   \r
1512     } else if (ch == '\'' || ch == '"') {\r
1513       // Quoting with ' ' or " ", with \ as escape character.\r
1514       // Inconvenient for long strings that may contain Windows filenames.\r
1515       start = ch;\r
1516       ch = get(cl);\r
1517       while (start) {\r
1518         switch (ch) {\r
1519         case NULLCHAR:\r
1520           start = NULLCHAR;\r
1521           break;\r
1522 \r
1523         default:\r
1524         not_special:\r
1525           *q++ = ch;\r
1526           ch = get(cl);\r
1527           break;\r
1528 \r
1529         case '\'':\r
1530         case '\"':\r
1531           if (ch == start) {\r
1532             ch = get(cl);\r
1533             start = NULLCHAR;\r
1534             break;\r
1535           } else {\r
1536             goto not_special;\r
1537           }\r
1538 \r
1539         case '\\':\r
1540           if (ad->argType == ArgFilename\r
1541               || ad->argType == ArgSettingsFilename) {\r
1542               goto not_special;\r
1543           }\r
1544           ch = get(cl);\r
1545           switch (ch) {\r
1546           case NULLCHAR:\r
1547             ExitArgError("Incomplete \\ escape in value for", argName);\r
1548             break;\r
1549           case 'n':\r
1550             *q++ = '\n';\r
1551             ch = get(cl);\r
1552             break;\r
1553           case 'r':\r
1554             *q++ = '\r';\r
1555             ch = get(cl);\r
1556             break;\r
1557           case 't':\r
1558             *q++ = '\t';\r
1559             ch = get(cl);\r
1560             break;\r
1561           case 'b':\r
1562             *q++ = '\b';\r
1563             ch = get(cl);\r
1564             break;\r
1565           case 'f':\r
1566             *q++ = '\f';\r
1567             ch = get(cl);\r
1568             break;\r
1569           default:\r
1570             octval = 0;\r
1571             for (i = 0; i < 3; i++) {\r
1572               if (ch >= '0' && ch <= '7') {\r
1573                 octval = octval*8 + (ch - '0');\r
1574                 ch = get(cl);\r
1575               } else {\r
1576                 break;\r
1577               }\r
1578             }\r
1579             if (i > 0) {\r
1580               *q++ = (char) octval;\r
1581             } else {\r
1582               *q++ = ch;\r
1583               ch = get(cl);\r
1584             }\r
1585             break;\r
1586           }\r
1587           break;\r
1588         }\r
1589       }\r
1590     } else {\r
1591       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1592         *q++ = ch;\r
1593         ch = get(cl);\r
1594       }\r
1595     }\r
1596     *q = NULLCHAR;\r
1597 \r
1598     switch (ad->argType) {\r
1599     case ArgInt:\r
1600       *(int *) ad->argLoc = atoi(argValue);\r
1601       break;\r
1602 \r
1603     case ArgFloat:\r
1604       *(float *) ad->argLoc = (float) atof(argValue);\r
1605       break;\r
1606 \r
1607     case ArgString:\r
1608     case ArgFilename:\r
1609       *(char **) ad->argLoc = strdup(argValue);\r
1610       break;\r
1611 \r
1612     case ArgSettingsFilename:\r
1613       {\r
1614         char fullname[MSG_SIZ];\r
1615         if (ParseSettingsFile(argValue, fullname)) {\r
1616           if (ad->argLoc != NULL) {\r
1617             *(char **) ad->argLoc = strdup(fullname);\r
1618           }\r
1619         } else {\r
1620           if (ad->argLoc != NULL) {\r
1621           } else {\r
1622             ExitArgError("Failed to open indirection file", argValue);\r
1623           }\r
1624         }\r
1625       }\r
1626       break;\r
1627 \r
1628     case ArgBoolean:\r
1629       switch (argValue[0]) {\r
1630       case 't':\r
1631       case 'T':\r
1632         *(Boolean *) ad->argLoc = TRUE;\r
1633         break;\r
1634       case 'f':\r
1635       case 'F':\r
1636         *(Boolean *) ad->argLoc = FALSE;\r
1637         break;\r
1638       default:\r
1639         ExitArgError("Unrecognized boolean argument value", argValue);\r
1640         break;\r
1641       }\r
1642       break;\r
1643 \r
1644     case ArgColor:\r
1645       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1646       break;\r
1647 \r
1648     case ArgAttribs: {\r
1649       ColorClass cc = (ColorClass)ad->argLoc;\r
1650       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1651       }\r
1652       break;\r
1653       \r
1654     case ArgBoardSize:\r
1655       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1656       break;\r
1657 \r
1658     case ArgFont:\r
1659       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1660       break;\r
1661 \r
1662     case ArgCommSettings:\r
1663       ParseCommSettings(argValue, &dcb);\r
1664       break;\r
1665 \r
1666     case ArgNone:\r
1667       ExitArgError("Unrecognized argument", argValue);\r
1668       break;\r
1669     }\r
1670   }\r
1671 }\r
1672 \r
1673 VOID\r
1674 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1675 {\r
1676   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1677   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1678   DeleteDC(hdc);\r
1679   lf->lfWidth = 0;\r
1680   lf->lfEscapement = 0;\r
1681   lf->lfOrientation = 0;\r
1682   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1683   lf->lfItalic = mfp->italic;\r
1684   lf->lfUnderline = mfp->underline;\r
1685   lf->lfStrikeOut = mfp->strikeout;\r
1686   lf->lfCharSet = DEFAULT_CHARSET;\r
1687   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1688   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1689   lf->lfQuality = DEFAULT_QUALITY;\r
1690   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1691   strcpy(lf->lfFaceName, mfp->faceName);\r
1692 }\r
1693 \r
1694 VOID\r
1695 CreateFontInMF(MyFont *mf)\r
1696 {\r
1697   LFfromMFP(&mf->lf, &mf->mfp);\r
1698   if (mf->hf) DeleteObject(mf->hf);\r
1699   mf->hf = CreateFontIndirect(&mf->lf);\r
1700 }\r
1701 \r
1702 VOID\r
1703 SetDefaultTextAttribs()\r
1704 {\r
1705   ColorClass cc;\r
1706   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1707     ParseAttribs(&textAttribs[cc].color, \r
1708                  &textAttribs[cc].effects, \r
1709                  defaultTextAttribs[cc]);\r
1710   }\r
1711 }\r
1712 \r
1713 VOID\r
1714 SetDefaultSounds()\r
1715 {\r
1716   ColorClass cc;\r
1717   SoundClass sc;\r
1718   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1719     textAttribs[cc].sound.name = strdup("");\r
1720     textAttribs[cc].sound.data = NULL;\r
1721   }\r
1722   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1723     sounds[sc].name = strdup("");\r
1724     sounds[sc].data = NULL;\r
1725   }\r
1726   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1727 }\r
1728 \r
1729 VOID\r
1730 LoadAllSounds()\r
1731 {\r
1732   ColorClass cc;\r
1733   SoundClass sc;\r
1734   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1735     MyLoadSound(&textAttribs[cc].sound);\r
1736   }\r
1737   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1738     MyLoadSound(&sounds[sc]);\r
1739   }\r
1740 }\r
1741 \r
1742 VOID\r
1743 InitAppData(LPSTR lpCmdLine)\r
1744 {\r
1745   int i, j;\r
1746   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1747   char *dummy, *p;\r
1748 \r
1749   programName = szAppName;\r
1750 \r
1751   /* Initialize to defaults */\r
1752   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1753   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1754   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1755   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1756   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1757   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1758   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1759   SetDefaultTextAttribs();\r
1760   SetDefaultSounds();\r
1761   appData.movesPerSession = MOVES_PER_SESSION;\r
1762   appData.initString = INIT_STRING;\r
1763   appData.secondInitString = INIT_STRING;\r
1764   appData.firstComputerString = COMPUTER_STRING;\r
1765   appData.secondComputerString = COMPUTER_STRING;\r
1766   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1767   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1768   appData.firstPlaysBlack = FALSE;\r
1769   appData.noChessProgram = FALSE;\r
1770   chessProgram = FALSE;\r
1771   appData.firstHost = FIRST_HOST;\r
1772   appData.secondHost = SECOND_HOST;\r
1773   appData.firstDirectory = FIRST_DIRECTORY;\r
1774   appData.secondDirectory = SECOND_DIRECTORY;\r
1775   appData.bitmapDirectory = "";\r
1776   appData.remoteShell = REMOTE_SHELL;\r
1777   appData.remoteUser = "";\r
1778   appData.timeDelay = TIME_DELAY;\r
1779   appData.timeControl = TIME_CONTROL;\r
1780   appData.timeIncrement = TIME_INCREMENT;\r
1781   appData.icsActive = FALSE;\r
1782   appData.icsHost = "";\r
1783   appData.icsPort = ICS_PORT;\r
1784   appData.icsCommPort = ICS_COMM_PORT;\r
1785   appData.icsLogon = ICS_LOGON;\r
1786   appData.icsHelper = "";\r
1787   appData.useTelnet = FALSE;\r
1788   appData.telnetProgram = TELNET_PROGRAM;\r
1789   appData.gateway = "";\r
1790   appData.loadGameFile = "";\r
1791   appData.loadGameIndex = 0;\r
1792   appData.saveGameFile = "";\r
1793   appData.autoSaveGames = FALSE;\r
1794   appData.loadPositionFile = "";\r
1795   appData.loadPositionIndex = 1;\r
1796   appData.savePositionFile = "";\r
1797   appData.matchMode = FALSE;\r
1798   appData.matchGames = 0;\r
1799   appData.monoMode = FALSE;\r
1800   appData.debugMode = FALSE;\r
1801   appData.clockMode = TRUE;\r
1802   boardSize = (BoardSize) -1; /* determine by screen size */\r
1803   appData.Iconic = FALSE; /*unused*/\r
1804   appData.searchTime = "";\r
1805   appData.searchDepth = 0;\r
1806   appData.showCoords = FALSE;\r
1807   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1808   appData.autoCallFlag = FALSE;\r
1809   appData.flipView = FALSE;\r
1810   appData.autoFlipView = TRUE;\r
1811   appData.cmailGameName = "";\r
1812   appData.alwaysPromoteToQueen = FALSE;\r
1813   appData.oldSaveStyle = FALSE;\r
1814   appData.quietPlay = FALSE;\r
1815   appData.showThinking = FALSE;\r
1816   appData.ponderNextMove = TRUE;\r
1817   appData.periodicUpdates = TRUE;\r
1818   appData.popupExitMessage = TRUE;\r
1819   appData.popupMoveErrors = FALSE;\r
1820   appData.autoObserve = FALSE;\r
1821   appData.autoComment = FALSE;\r
1822   appData.animate = TRUE;\r
1823   appData.animSpeed = 10;\r
1824   appData.animateDragging = TRUE;\r
1825   appData.highlightLastMove = TRUE;\r
1826   appData.getMoveList = TRUE;\r
1827   appData.testLegality = TRUE;\r
1828   appData.premove = TRUE;\r
1829   appData.premoveWhite = FALSE;\r
1830   appData.premoveWhiteText = "";\r
1831   appData.premoveBlack = FALSE;\r
1832   appData.premoveBlackText = "";\r
1833   appData.icsAlarm = TRUE;\r
1834   appData.icsAlarmTime = 5000;\r
1835   appData.autoRaiseBoard = TRUE;\r
1836   appData.localLineEditing = TRUE;\r
1837   appData.colorize = TRUE;\r
1838   appData.reuseFirst = TRUE;\r
1839   appData.reuseSecond = TRUE;\r
1840   appData.blindfold = FALSE;\r
1841   dcb.DCBlength = sizeof(DCB);\r
1842   dcb.BaudRate = 9600;\r
1843   dcb.fBinary = TRUE;\r
1844   dcb.fParity = FALSE;\r
1845   dcb.fOutxCtsFlow = FALSE;\r
1846   dcb.fOutxDsrFlow = FALSE;\r
1847   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1848   dcb.fDsrSensitivity = FALSE;\r
1849   dcb.fTXContinueOnXoff = TRUE;\r
1850   dcb.fOutX = FALSE;\r
1851   dcb.fInX = FALSE;\r
1852   dcb.fNull = FALSE;\r
1853   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1854   dcb.fAbortOnError = FALSE;\r
1855   dcb.wReserved = 0;\r
1856   dcb.ByteSize = 7;\r
1857   dcb.Parity = SPACEPARITY;\r
1858   dcb.StopBits = ONESTOPBIT;\r
1859   settingsFileName = SETTINGS_FILE;\r
1860   saveSettingsOnExit = TRUE;\r
1861   boardX = CW_USEDEFAULT;\r
1862   boardY = CW_USEDEFAULT;\r
1863   consoleX = CW_USEDEFAULT; \r
1864   consoleY = CW_USEDEFAULT; \r
1865   consoleW = CW_USEDEFAULT;\r
1866   consoleH = CW_USEDEFAULT;\r
1867   analysisX = CW_USEDEFAULT; \r
1868   analysisY = CW_USEDEFAULT; \r
1869   analysisW = CW_USEDEFAULT;\r
1870   analysisH = CW_USEDEFAULT;\r
1871   commentX = CW_USEDEFAULT; \r
1872   commentY = CW_USEDEFAULT; \r
1873   commentW = CW_USEDEFAULT;\r
1874   commentH = CW_USEDEFAULT;\r
1875   editTagsX = CW_USEDEFAULT; \r
1876   editTagsY = CW_USEDEFAULT; \r
1877   editTagsW = CW_USEDEFAULT;\r
1878   editTagsH = CW_USEDEFAULT;\r
1879   gameListX = CW_USEDEFAULT; \r
1880   gameListY = CW_USEDEFAULT; \r
1881   gameListW = CW_USEDEFAULT;\r
1882   gameListH = CW_USEDEFAULT;\r
1883   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1884   icsNames = ICS_NAMES;\r
1885   firstChessProgramNames = FCP_NAMES;\r
1886   secondChessProgramNames = SCP_NAMES;\r
1887   appData.initialMode = "";\r
1888   appData.variant = "normal";\r
1889   appData.firstProtocolVersion = PROTOVER;\r
1890   appData.secondProtocolVersion = PROTOVER;\r
1891   appData.showButtonBar = TRUE;\r
1892 \r
1893    /* [AS] New properties (see comments in header file) */\r
1894   appData.firstScoreIsAbsolute = FALSE;\r
1895   appData.secondScoreIsAbsolute = FALSE;\r
1896   appData.saveExtendedInfoInPGN = FALSE;\r
1897   appData.hideThinkingFromHuman = FALSE;\r
1898   appData.liteBackTextureFile = "";\r
1899   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1900   appData.darkBackTextureFile = "";\r
1901   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1902   appData.renderPiecesWithFont = "";\r
1903   appData.fontToPieceTable = "";\r
1904   appData.fontBackColorWhite = 0;\r
1905   appData.fontForeColorWhite = 0;\r
1906   appData.fontBackColorBlack = 0;\r
1907   appData.fontForeColorBlack = 0;\r
1908   appData.fontPieceSize = 80;\r
1909   appData.overrideLineGap = 1;\r
1910   appData.adjudicateLossThreshold = 0;\r
1911   appData.delayBeforeQuit = 0;\r
1912   appData.delayAfterQuit = 0;\r
1913   appData.nameOfDebugFile = "winboard.debug";\r
1914   appData.pgnEventHeader = "Computer Chess Game";\r
1915   appData.defaultFrcPosition = -1;\r
1916   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1917   appData.saveOutOfBookInfo = TRUE;\r
1918   appData.showEvalInMoveHistory = TRUE;\r
1919   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1920   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1921   appData.highlightMoveWithArrow = FALSE;\r
1922   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1923   appData.useStickyWindows = TRUE;\r
1924   appData.adjudicateDrawMoves = 0;\r
1925   appData.autoDisplayComment = TRUE;\r
1926   appData.autoDisplayTags = TRUE;\r
1927   appData.firstIsUCI = FALSE;\r
1928   appData.secondIsUCI = FALSE;\r
1929   appData.firstHasOwnBookUCI = TRUE;\r
1930   appData.secondHasOwnBookUCI = TRUE;\r
1931   appData.polyglotDir = "";\r
1932   appData.usePolyglotBook = FALSE;\r
1933   appData.polyglotBook = "";\r
1934   appData.defaultHashSize = 64;\r
1935   appData.defaultCacheSizeEGTB = 4;\r
1936   appData.defaultPathEGTB = "c:\\egtb";\r
1937 \r
1938   InitWindowPlacement( &wpMoveHistory );\r
1939   InitWindowPlacement( &wpEvalGraph );\r
1940   InitWindowPlacement( &wpEngineOutput );\r
1941 \r
1942   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1943   appData.NrFiles      = -1;\r
1944   appData.NrRanks      = -1;\r
1945   appData.holdingsSize = -1;\r
1946   appData.testClaims   = FALSE;\r
1947   appData.checkMates   = FALSE;\r
1948   appData.materialDraws= FALSE;\r
1949   appData.trivialDraws = FALSE;\r
1950   appData.ruleMoves    = 51;\r
1951   appData.drawRepeats  = 6;\r
1952   appData.matchPause   = 10000;\r
1953   appData.alphaRank    = FALSE;\r
1954   appData.allWhite     = FALSE;\r
1955   appData.upsideDown   = FALSE;\r
1956   appData.serverPause  = 15;\r
1957   appData.serverMovesName   = NULL;\r
1958   appData.suppressLoadMoves = FALSE;\r
1959   appData.firstTimeOdds  = 1;\r
1960   appData.secondTimeOdds = 1;\r
1961   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1962   appData.secondAccumulateTC = 1;\r
1963   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1964   appData.secondNPS = -1;\r
1965   appData.engineComments = 1;\r
1966   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1967   appData.egtFormats = "";\r
1968 \r
1969 #ifdef ZIPPY\r
1970   appData.zippyTalk = ZIPPY_TALK;\r
1971   appData.zippyPlay = ZIPPY_PLAY;\r
1972   appData.zippyLines = ZIPPY_LINES;\r
1973   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1974   appData.zippyPassword = ZIPPY_PASSWORD;\r
1975   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1976   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1977   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1978   appData.zippyUseI = ZIPPY_USE_I;\r
1979   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1980   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1981   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1982   appData.zippyGameStart = ZIPPY_GAME_START;\r
1983   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1984   appData.zippyAbort = ZIPPY_ABORT;\r
1985   appData.zippyVariants = ZIPPY_VARIANTS;\r
1986   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1987   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1988 #endif\r
1989 \r
1990   /* Point font array elements to structures and\r
1991      parse default font names */\r
1992   for (i=0; i<NUM_FONTS; i++) {\r
1993     for (j=0; j<NUM_SIZES; j++) {\r
1994       font[j][i] = &fontRec[j][i];\r
1995       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1996     }\r
1997   }\r
1998   \r
1999   /* Parse default settings file if any */\r
2000   if (ParseSettingsFile(settingsFileName, buf)) {\r
2001     settingsFileName = strdup(buf);\r
2002   }\r
2003 \r
2004   /* Parse command line */\r
2005   ParseArgs(StringGet, &lpCmdLine);\r
2006 \r
2007   /* [HGM] make sure board size is acceptable */\r
2008   if(appData.NrFiles > BOARD_SIZE ||\r
2009      appData.NrRanks > BOARD_SIZE   )\r
2010       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2011 \r
2012   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2013    * with options from the command line, we now make an even higher priority\r
2014    * overrule by WB options attached to the engine command line. This so that\r
2015    * tournament managers can use WB options (such as /timeOdds) that follow\r
2016    * the engines.\r
2017    */\r
2018   if(appData.firstChessProgram != NULL) {\r
2019       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2020       static char *f = "first";\r
2021       char buf[MSG_SIZ], *q = buf;\r
2022       if(p != NULL) { // engine command line contains WinBoard options\r
2023           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2024           ParseArgs(StringGet, &q);\r
2025           p[-1] = 0; // cut them offengine command line\r
2026       }\r
2027   }\r
2028   // now do same for second chess program\r
2029   if(appData.secondChessProgram != NULL) {\r
2030       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2031       static char *s = "second";\r
2032       char buf[MSG_SIZ], *q = buf;\r
2033       if(p != NULL) { // engine command line contains WinBoard options\r
2034           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2035           ParseArgs(StringGet, &q);\r
2036           p[-1] = 0; // cut them offengine command line\r
2037       }\r
2038   }\r
2039 \r
2040 \r
2041   /* Propagate options that affect others */\r
2042   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2043   if (appData.icsActive || appData.noChessProgram) {\r
2044      chessProgram = FALSE;  /* not local chess program mode */\r
2045   }\r
2046 \r
2047   /* Open startup dialog if needed */\r
2048   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2049       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2050       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2051                         *appData.secondChessProgram == NULLCHAR))) {\r
2052     FARPROC lpProc;\r
2053     \r
2054     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2055     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2056     FreeProcInstance(lpProc);\r
2057   }\r
2058 \r
2059   /* Make sure save files land in the right (?) directory */\r
2060   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2061     appData.saveGameFile = strdup(buf);\r
2062   }\r
2063   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2064     appData.savePositionFile = strdup(buf);\r
2065   }\r
2066 \r
2067   /* Finish initialization for fonts and sounds */\r
2068   for (i=0; i<NUM_FONTS; i++) {\r
2069     for (j=0; j<NUM_SIZES; j++) {\r
2070       CreateFontInMF(font[j][i]);\r
2071     }\r
2072   }\r
2073   /* xboard, and older WinBoards, controlled the move sound with the\r
2074      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2075      always turn the option on (so that the backend will call us),\r
2076      then let the user turn the sound off by setting it to silence if\r
2077      desired.  To accommodate old winboard.ini files saved by old\r
2078      versions of WinBoard, we also turn off the sound if the option\r
2079      was initially set to false. */\r
2080   if (!appData.ringBellAfterMoves) {\r
2081     sounds[(int)SoundMove].name = strdup("");\r
2082     appData.ringBellAfterMoves = TRUE;\r
2083   }\r
2084   GetCurrentDirectory(MSG_SIZ, currDir);\r
2085   SetCurrentDirectory(installDir);\r
2086   LoadAllSounds();\r
2087   SetCurrentDirectory(currDir);\r
2088 \r
2089   p = icsTextMenuString;\r
2090   if (p[0] == '@') {\r
2091     FILE* f = fopen(p + 1, "r");\r
2092     if (f == NULL) {\r
2093       DisplayFatalError(p + 1, errno, 2);\r
2094       return;\r
2095     }\r
2096     i = fread(buf, 1, sizeof(buf)-1, f);\r
2097     fclose(f);\r
2098     buf[i] = NULLCHAR;\r
2099     p = buf;\r
2100   }\r
2101   ParseIcsTextMenu(strdup(p));\r
2102 }\r
2103 \r
2104 \r
2105 VOID\r
2106 InitMenuChecks()\r
2107 {\r
2108   HMENU hmenu = GetMenu(hwndMain);\r
2109 \r
2110   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2111                         MF_BYCOMMAND|((appData.icsActive &&\r
2112                                        *appData.icsCommPort != NULLCHAR) ?\r
2113                                       MF_ENABLED : MF_GRAYED));\r
2114   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2115                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2116                                      MF_CHECKED : MF_UNCHECKED));\r
2117 }\r
2118 \r
2119 \r
2120 VOID\r
2121 SaveSettings(char* name)\r
2122 {\r
2123   FILE *f;\r
2124   ArgDescriptor *ad;\r
2125   WINDOWPLACEMENT wp;\r
2126   char dir[MSG_SIZ];\r
2127 \r
2128   if (!hwndMain) return;\r
2129 \r
2130   GetCurrentDirectory(MSG_SIZ, dir);\r
2131   SetCurrentDirectory(installDir);\r
2132   f = fopen(name, "w");\r
2133   SetCurrentDirectory(dir);\r
2134   if (f == NULL) {\r
2135     DisplayError(name, errno);\r
2136     return;\r
2137   }\r
2138   fprintf(f, ";\n");\r
2139   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2140   fprintf(f, ";\n");\r
2141   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2142   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2143   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2144   fprintf(f, ";\n");\r
2145 \r
2146   wp.length = sizeof(WINDOWPLACEMENT);\r
2147   GetWindowPlacement(hwndMain, &wp);\r
2148   boardX = wp.rcNormalPosition.left;\r
2149   boardY = wp.rcNormalPosition.top;\r
2150 \r
2151   if (hwndConsole) {\r
2152     GetWindowPlacement(hwndConsole, &wp);\r
2153     consoleX = wp.rcNormalPosition.left;\r
2154     consoleY = wp.rcNormalPosition.top;\r
2155     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2156     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2157   }\r
2158 \r
2159   if (analysisDialog) {\r
2160     GetWindowPlacement(analysisDialog, &wp);\r
2161     analysisX = wp.rcNormalPosition.left;\r
2162     analysisY = wp.rcNormalPosition.top;\r
2163     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2164     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2165   }\r
2166 \r
2167   if (commentDialog) {\r
2168     GetWindowPlacement(commentDialog, &wp);\r
2169     commentX = wp.rcNormalPosition.left;\r
2170     commentY = wp.rcNormalPosition.top;\r
2171     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2172     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2173   }\r
2174 \r
2175   if (editTagsDialog) {\r
2176     GetWindowPlacement(editTagsDialog, &wp);\r
2177     editTagsX = wp.rcNormalPosition.left;\r
2178     editTagsY = wp.rcNormalPosition.top;\r
2179     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2180     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2181   }\r
2182 \r
2183   if (gameListDialog) {\r
2184     GetWindowPlacement(gameListDialog, &wp);\r
2185     gameListX = wp.rcNormalPosition.left;\r
2186     gameListY = wp.rcNormalPosition.top;\r
2187     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2188     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2189   }\r
2190 \r
2191   /* [AS] Move history */\r
2192   wpMoveHistory.visible = MoveHistoryIsUp();\r
2193   \r
2194   if( moveHistoryDialog ) {\r
2195     GetWindowPlacement(moveHistoryDialog, &wp);\r
2196     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2197     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2198     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2199     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2200   }\r
2201 \r
2202   /* [AS] Eval graph */\r
2203   wpEvalGraph.visible = EvalGraphIsUp();\r
2204 \r
2205   if( evalGraphDialog ) {\r
2206     GetWindowPlacement(evalGraphDialog, &wp);\r
2207     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2208     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2209     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2210     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2211   }\r
2212 \r
2213   /* [AS] Engine output */\r
2214   wpEngineOutput.visible = EngineOutputIsUp();\r
2215 \r
2216   if( engineOutputDialog ) {\r
2217     GetWindowPlacement(engineOutputDialog, &wp);\r
2218     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2219     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2220     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2221     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2222   }\r
2223 \r
2224   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2225     if (!ad->save) continue;\r
2226     switch (ad->argType) {\r
2227     case ArgString:\r
2228       {\r
2229         char *p = *(char **)ad->argLoc;\r
2230         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2231           /* Quote multiline values or \-containing values\r
2232              with { } if possible */\r
2233           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2234         } else {\r
2235           /* Else quote with " " */\r
2236           fprintf(f, "/%s=\"", ad->argName);\r
2237           while (*p) {\r
2238             if (*p == '\n') fprintf(f, "\n");\r
2239             else if (*p == '\r') fprintf(f, "\\r");\r
2240             else if (*p == '\t') fprintf(f, "\\t");\r
2241             else if (*p == '\b') fprintf(f, "\\b");\r
2242             else if (*p == '\f') fprintf(f, "\\f");\r
2243             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2244             else if (*p == '\"') fprintf(f, "\\\"");\r
2245             else if (*p == '\\') fprintf(f, "\\\\");\r
2246             else putc(*p, f);\r
2247             p++;\r
2248           }\r
2249           fprintf(f, "\"\n");\r
2250         }\r
2251       }\r
2252       break;\r
2253     case ArgInt:\r
2254       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2255       break;\r
2256     case ArgFloat:\r
2257       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2258       break;\r
2259     case ArgBoolean:\r
2260       fprintf(f, "/%s=%s\n", ad->argName, \r
2261         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2262       break;\r
2263     case ArgTrue:\r
2264       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2265       break;\r
2266     case ArgFalse:\r
2267       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2268       break;\r
2269     case ArgColor:\r
2270       {\r
2271         COLORREF color = *(COLORREF *)ad->argLoc;\r
2272         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2273           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2274       }\r
2275       break;\r
2276     case ArgAttribs:\r
2277       {\r
2278         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2279         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2280           (ta->effects & CFE_BOLD) ? "b" : "",\r
2281           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2282           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2283           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2284           (ta->effects) ? " " : "",\r
2285           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2286       }\r
2287       break;\r
2288     case ArgFilename:\r
2289       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2290         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2291       } else {\r
2292         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2293       }\r
2294       break;\r
2295     case ArgBoardSize:\r
2296       fprintf(f, "/%s=%s\n", ad->argName,\r
2297               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2298       break;\r
2299     case ArgFont:\r
2300       {\r
2301         int bs;\r
2302         for (bs=0; bs<NUM_SIZES; bs++) {\r
2303           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2304           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2305           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2306             ad->argName, mfp->faceName, mfp->pointSize,\r
2307             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2308             mfp->bold ? "b" : "",\r
2309             mfp->italic ? "i" : "",\r
2310             mfp->underline ? "u" : "",\r
2311             mfp->strikeout ? "s" : "");\r
2312         }\r
2313       }\r
2314       break;\r
2315     case ArgCommSettings:\r
2316       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2317     }\r
2318   }\r
2319   fclose(f);\r
2320 }\r
2321 \r
2322 \r
2323 \r
2324 /*---------------------------------------------------------------------------*\\r
2325  *\r
2326  * GDI board drawing routines\r
2327  *\r
2328 \*---------------------------------------------------------------------------*/\r
2329 \r
2330 /* [AS] Draw square using background texture */\r
2331 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2332 {\r
2333     XFORM   x;\r
2334 \r
2335     if( mode == 0 ) {\r
2336         return; /* Should never happen! */\r
2337     }\r
2338 \r
2339     SetGraphicsMode( dst, GM_ADVANCED );\r
2340 \r
2341     switch( mode ) {\r
2342     case 1:\r
2343         /* Identity */\r
2344         break;\r
2345     case 2:\r
2346         /* X reflection */\r
2347         x.eM11 = -1.0;\r
2348         x.eM12 = 0;\r
2349         x.eM21 = 0;\r
2350         x.eM22 = 1.0;\r
2351         x.eDx = (FLOAT) dw + dx - 1;\r
2352         x.eDy = 0;\r
2353         dx = 0;\r
2354         SetWorldTransform( dst, &x );\r
2355         break;\r
2356     case 3:\r
2357         /* Y reflection */\r
2358         x.eM11 = 1.0;\r
2359         x.eM12 = 0;\r
2360         x.eM21 = 0;\r
2361         x.eM22 = -1.0;\r
2362         x.eDx = 0;\r
2363         x.eDy = (FLOAT) dh + dy - 1;\r
2364         dy = 0;\r
2365         SetWorldTransform( dst, &x );\r
2366         break;\r
2367     case 4:\r
2368         /* X/Y flip */\r
2369         x.eM11 = 0;\r
2370         x.eM12 = 1.0;\r
2371         x.eM21 = 1.0;\r
2372         x.eM22 = 0;\r
2373         x.eDx = (FLOAT) dx;\r
2374         x.eDy = (FLOAT) dy;\r
2375         dx = 0;\r
2376         dy = 0;\r
2377         SetWorldTransform( dst, &x );\r
2378         break;\r
2379     }\r
2380 \r
2381     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2382 \r
2383     x.eM11 = 1.0;\r
2384     x.eM12 = 0;\r
2385     x.eM21 = 0;\r
2386     x.eM22 = 1.0;\r
2387     x.eDx = 0;\r
2388     x.eDy = 0;\r
2389     SetWorldTransform( dst, &x );\r
2390 \r
2391     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2392 }\r
2393 \r
2394 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2395 enum {\r
2396     PM_WP = (int) WhitePawn, \r
2397     PM_WN = (int) WhiteKnight, \r
2398     PM_WB = (int) WhiteBishop, \r
2399     PM_WR = (int) WhiteRook, \r
2400     PM_WQ = (int) WhiteQueen, \r
2401     PM_WF = (int) WhiteFerz, \r
2402     PM_WW = (int) WhiteWazir, \r
2403     PM_WE = (int) WhiteAlfil, \r
2404     PM_WM = (int) WhiteMan, \r
2405     PM_WO = (int) WhiteCannon, \r
2406     PM_WU = (int) WhiteUnicorn, \r
2407     PM_WH = (int) WhiteNightrider, \r
2408     PM_WA = (int) WhiteAngel, \r
2409     PM_WC = (int) WhiteMarshall, \r
2410     PM_WAB = (int) WhiteCardinal, \r
2411     PM_WD = (int) WhiteDragon, \r
2412     PM_WL = (int) WhiteLance, \r
2413     PM_WS = (int) WhiteCobra, \r
2414     PM_WV = (int) WhiteFalcon, \r
2415     PM_WSG = (int) WhiteSilver, \r
2416     PM_WG = (int) WhiteGrasshopper, \r
2417     PM_WK = (int) WhiteKing,\r
2418     PM_BP = (int) BlackPawn, \r
2419     PM_BN = (int) BlackKnight, \r
2420     PM_BB = (int) BlackBishop, \r
2421     PM_BR = (int) BlackRook, \r
2422     PM_BQ = (int) BlackQueen, \r
2423     PM_BF = (int) BlackFerz, \r
2424     PM_BW = (int) BlackWazir, \r
2425     PM_BE = (int) BlackAlfil, \r
2426     PM_BM = (int) BlackMan,\r
2427     PM_BO = (int) BlackCannon, \r
2428     PM_BU = (int) BlackUnicorn, \r
2429     PM_BH = (int) BlackNightrider, \r
2430     PM_BA = (int) BlackAngel, \r
2431     PM_BC = (int) BlackMarshall, \r
2432     PM_BG = (int) BlackGrasshopper, \r
2433     PM_BAB = (int) BlackCardinal,\r
2434     PM_BD = (int) BlackDragon,\r
2435     PM_BL = (int) BlackLance,\r
2436     PM_BS = (int) BlackCobra,\r
2437     PM_BV = (int) BlackFalcon,\r
2438     PM_BSG = (int) BlackSilver,\r
2439     PM_BK = (int) BlackKing\r
2440 };\r
2441 \r
2442 static HFONT hPieceFont = NULL;\r
2443 static HBITMAP hPieceMask[(int) EmptySquare];\r
2444 static HBITMAP hPieceFace[(int) EmptySquare];\r
2445 static int fontBitmapSquareSize = 0;\r
2446 static char pieceToFontChar[(int) EmptySquare] =\r
2447                               { 'p', 'n', 'b', 'r', 'q', \r
2448                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2449                       'k', 'o', 'm', 'v', 't', 'w', \r
2450                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2451                                                               'l' };\r
2452 \r
2453 extern BOOL SetCharTable( char *table, const char * map );\r
2454 /* [HGM] moved to backend.c */\r
2455 \r
2456 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2457 {\r
2458     HBRUSH hbrush;\r
2459     BYTE r1 = GetRValue( color );\r
2460     BYTE g1 = GetGValue( color );\r
2461     BYTE b1 = GetBValue( color );\r
2462     BYTE r2 = r1 / 2;\r
2463     BYTE g2 = g1 / 2;\r
2464     BYTE b2 = b1 / 2;\r
2465     RECT rc;\r
2466 \r
2467     /* Create a uniform background first */\r
2468     hbrush = CreateSolidBrush( color );\r
2469     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2470     FillRect( hdc, &rc, hbrush );\r
2471     DeleteObject( hbrush );\r
2472     \r
2473     if( mode == 1 ) {\r
2474         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2475         int steps = squareSize / 2;\r
2476         int i;\r
2477 \r
2478         for( i=0; i<steps; i++ ) {\r
2479             BYTE r = r1 - (r1-r2) * i / steps;\r
2480             BYTE g = g1 - (g1-g2) * i / steps;\r
2481             BYTE b = b1 - (b1-b2) * i / steps;\r
2482 \r
2483             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2484             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2485             FillRect( hdc, &rc, hbrush );\r
2486             DeleteObject(hbrush);\r
2487         }\r
2488     }\r
2489     else if( mode == 2 ) {\r
2490         /* Diagonal gradient, good more or less for every piece */\r
2491         POINT triangle[3];\r
2492         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2493         HBRUSH hbrush_old;\r
2494         int steps = squareSize;\r
2495         int i;\r
2496 \r
2497         triangle[0].x = squareSize - steps;\r
2498         triangle[0].y = squareSize;\r
2499         triangle[1].x = squareSize;\r
2500         triangle[1].y = squareSize;\r
2501         triangle[2].x = squareSize;\r
2502         triangle[2].y = squareSize - steps;\r
2503 \r
2504         for( i=0; i<steps; i++ ) {\r
2505             BYTE r = r1 - (r1-r2) * i / steps;\r
2506             BYTE g = g1 - (g1-g2) * i / steps;\r
2507             BYTE b = b1 - (b1-b2) * i / steps;\r
2508 \r
2509             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2510             hbrush_old = SelectObject( hdc, hbrush );\r
2511             Polygon( hdc, triangle, 3 );\r
2512             SelectObject( hdc, hbrush_old );\r
2513             DeleteObject(hbrush);\r
2514             triangle[0].x++;\r
2515             triangle[2].y++;\r
2516         }\r
2517 \r
2518         SelectObject( hdc, hpen );\r
2519     }\r
2520 }\r
2521 \r
2522 /*\r
2523     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2524     seems to work ok. The main problem here is to find the "inside" of a chess\r
2525     piece: follow the steps as explained below.\r
2526 */\r
2527 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2528 {\r
2529     HBITMAP hbm;\r
2530     HBITMAP hbm_old;\r
2531     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2532     RECT rc;\r
2533     SIZE sz;\r
2534     POINT pt;\r
2535     int backColor = whitePieceColor; \r
2536     int foreColor = blackPieceColor;\r
2537     \r
2538     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2539         backColor = appData.fontBackColorWhite;\r
2540         foreColor = appData.fontForeColorWhite;\r
2541     }\r
2542     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2543         backColor = appData.fontBackColorBlack;\r
2544         foreColor = appData.fontForeColorBlack;\r
2545     }\r
2546 \r
2547     /* Mask */\r
2548     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2549 \r
2550     hbm_old = SelectObject( hdc, hbm );\r
2551 \r
2552     rc.left = 0;\r
2553     rc.top = 0;\r
2554     rc.right = squareSize;\r
2555     rc.bottom = squareSize;\r
2556 \r
2557     /* Step 1: background is now black */\r
2558     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2559 \r
2560     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2561 \r
2562     pt.x = (squareSize - sz.cx) / 2;\r
2563     pt.y = (squareSize - sz.cy) / 2;\r
2564 \r
2565     SetBkMode( hdc, TRANSPARENT );\r
2566     SetTextColor( hdc, chroma );\r
2567     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2568     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2569 \r
2570     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2571     /* Step 3: the area outside the piece is filled with white */\r
2572 //    FloodFill( hdc, 0, 0, chroma );\r
2573     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2574     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2575     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2576     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2577     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2578     /* \r
2579         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2580         but if the start point is not inside the piece we're lost!\r
2581         There should be a better way to do this... if we could create a region or path\r
2582         from the fill operation we would be fine for example.\r
2583     */\r
2584 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2585     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2586 \r
2587     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2588         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2589         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2590 \r
2591         SelectObject( dc2, bm2 );\r
2592         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2593         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2594         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2595         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2596         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2597 \r
2598         DeleteDC( dc2 );\r
2599         DeleteObject( bm2 );\r
2600     }\r
2601 \r
2602     SetTextColor( hdc, 0 );\r
2603     /* \r
2604         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2605         draw the piece again in black for safety.\r
2606     */\r
2607     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2608 \r
2609     SelectObject( hdc, hbm_old );\r
2610 \r
2611     if( hPieceMask[index] != NULL ) {\r
2612         DeleteObject( hPieceMask[index] );\r
2613     }\r
2614 \r
2615     hPieceMask[index] = hbm;\r
2616 \r
2617     /* Face */\r
2618     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2619 \r
2620     SelectObject( hdc, hbm );\r
2621 \r
2622     {\r
2623         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2624         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2625         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2626 \r
2627         SelectObject( dc1, hPieceMask[index] );\r
2628         SelectObject( dc2, bm2 );\r
2629         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2630         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2631         \r
2632         /* \r
2633             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2634             the piece background and deletes (makes transparent) the rest.\r
2635             Thanks to that mask, we are free to paint the background with the greates\r
2636             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2637             We use this, to make gradients and give the pieces a "roundish" look.\r
2638         */\r
2639         SetPieceBackground( hdc, backColor, 2 );\r
2640         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2641 \r
2642         DeleteDC( dc2 );\r
2643         DeleteDC( dc1 );\r
2644         DeleteObject( bm2 );\r
2645     }\r
2646 \r
2647     SetTextColor( hdc, foreColor );\r
2648     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2649 \r
2650     SelectObject( hdc, hbm_old );\r
2651 \r
2652     if( hPieceFace[index] != NULL ) {\r
2653         DeleteObject( hPieceFace[index] );\r
2654     }\r
2655 \r
2656     hPieceFace[index] = hbm;\r
2657 }\r
2658 \r
2659 static int TranslatePieceToFontPiece( int piece )\r
2660 {\r
2661     switch( piece ) {\r
2662     case BlackPawn:\r
2663         return PM_BP;\r
2664     case BlackKnight:\r
2665         return PM_BN;\r
2666     case BlackBishop:\r
2667         return PM_BB;\r
2668     case BlackRook:\r
2669         return PM_BR;\r
2670     case BlackQueen:\r
2671         return PM_BQ;\r
2672     case BlackKing:\r
2673         return PM_BK;\r
2674     case WhitePawn:\r
2675         return PM_WP;\r
2676     case WhiteKnight:\r
2677         return PM_WN;\r
2678     case WhiteBishop:\r
2679         return PM_WB;\r
2680     case WhiteRook:\r
2681         return PM_WR;\r
2682     case WhiteQueen:\r
2683         return PM_WQ;\r
2684     case WhiteKing:\r
2685         return PM_WK;\r
2686 \r
2687     case BlackAngel:\r
2688         return PM_BA;\r
2689     case BlackMarshall:\r
2690         return PM_BC;\r
2691     case BlackFerz:\r
2692         return PM_BF;\r
2693     case BlackNightrider:\r
2694         return PM_BH;\r
2695     case BlackAlfil:\r
2696         return PM_BE;\r
2697     case BlackWazir:\r
2698         return PM_BW;\r
2699     case BlackUnicorn:\r
2700         return PM_BU;\r
2701     case BlackCannon:\r
2702         return PM_BO;\r
2703     case BlackGrasshopper:\r
2704         return PM_BG;\r
2705     case BlackMan:\r
2706         return PM_BM;\r
2707     case BlackSilver:\r
2708         return PM_BSG;\r
2709     case BlackLance:\r
2710         return PM_BL;\r
2711     case BlackFalcon:\r
2712         return PM_BV;\r
2713     case BlackCobra:\r
2714         return PM_BS;\r
2715     case BlackCardinal:\r
2716         return PM_BAB;\r
2717     case BlackDragon:\r
2718         return PM_BD;\r
2719 \r
2720     case WhiteAngel:\r
2721         return PM_WA;\r
2722     case WhiteMarshall:\r
2723         return PM_WC;\r
2724     case WhiteFerz:\r
2725         return PM_WF;\r
2726     case WhiteNightrider:\r
2727         return PM_WH;\r
2728     case WhiteAlfil:\r
2729         return PM_WE;\r
2730     case WhiteWazir:\r
2731         return PM_WW;\r
2732     case WhiteUnicorn:\r
2733         return PM_WU;\r
2734     case WhiteCannon:\r
2735         return PM_WO;\r
2736     case WhiteGrasshopper:\r
2737         return PM_WG;\r
2738     case WhiteMan:\r
2739         return PM_WM;\r
2740     case WhiteSilver:\r
2741         return PM_WSG;\r
2742     case WhiteLance:\r
2743         return PM_WL;\r
2744     case WhiteFalcon:\r
2745         return PM_WV;\r
2746     case WhiteCobra:\r
2747         return PM_WS;\r
2748     case WhiteCardinal:\r
2749         return PM_WAB;\r
2750     case WhiteDragon:\r
2751         return PM_WD;\r
2752     }\r
2753 \r
2754     return 0;\r
2755 }\r
2756 \r
2757 void CreatePiecesFromFont()\r
2758 {\r
2759     LOGFONT lf;\r
2760     HDC hdc_window = NULL;\r
2761     HDC hdc = NULL;\r
2762     HFONT hfont_old;\r
2763     int fontHeight;\r
2764     int i;\r
2765 \r
2766     if( fontBitmapSquareSize < 0 ) {\r
2767         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2768         return;\r
2769     }\r
2770 \r
2771     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2772         fontBitmapSquareSize = -1;\r
2773         return;\r
2774     }\r
2775 \r
2776     if( fontBitmapSquareSize != squareSize ) {\r
2777         hdc_window = GetDC( hwndMain );\r
2778         hdc = CreateCompatibleDC( hdc_window );\r
2779 \r
2780         if( hPieceFont != NULL ) {\r
2781             DeleteObject( hPieceFont );\r
2782         }\r
2783         else {\r
2784             for( i=0; i<=(int)BlackKing; i++ ) {\r
2785                 hPieceMask[i] = NULL;\r
2786                 hPieceFace[i] = NULL;\r
2787             }\r
2788         }\r
2789 \r
2790         fontHeight = 75;\r
2791 \r
2792         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2793             fontHeight = appData.fontPieceSize;\r
2794         }\r
2795 \r
2796         fontHeight = (fontHeight * squareSize) / 100;\r
2797 \r
2798         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2799         lf.lfWidth = 0;\r
2800         lf.lfEscapement = 0;\r
2801         lf.lfOrientation = 0;\r
2802         lf.lfWeight = FW_NORMAL;\r
2803         lf.lfItalic = 0;\r
2804         lf.lfUnderline = 0;\r
2805         lf.lfStrikeOut = 0;\r
2806         lf.lfCharSet = DEFAULT_CHARSET;\r
2807         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2808         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2809         lf.lfQuality = PROOF_QUALITY;\r
2810         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2811         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2812         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2813 \r
2814         hPieceFont = CreateFontIndirect( &lf );\r
2815 \r
2816         if( hPieceFont == NULL ) {\r
2817             fontBitmapSquareSize = -2;\r
2818         }\r
2819         else {\r
2820             /* Setup font-to-piece character table */\r
2821             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2822                 /* No (or wrong) global settings, try to detect the font */\r
2823                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2824                     /* Alpha */\r
2825                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2826                 }\r
2827                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2828                     /* DiagramTT* family */\r
2829                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2830                 }\r
2831                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2832                     /* Fairy symbols */\r
2833                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2834                 }\r
2835                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2836                     /* Good Companion (Some characters get warped as literal :-( */\r
2837                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2838                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2839                     SetCharTable(pieceToFontChar, s);\r
2840                 }\r
2841                 else {\r
2842                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2843                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2844                 }\r
2845             }\r
2846 \r
2847             /* Create bitmaps */\r
2848             hfont_old = SelectObject( hdc, hPieceFont );\r
2849 #if 0\r
2850             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2851             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2852             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2853             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2854             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2855             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2856             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2857             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2858             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2859             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2860             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2861             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2862 \r
2863             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2864             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2865             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2866             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2867             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2868             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2869             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2870             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2875             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2895 #else\r
2896             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2897                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2898                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2899 #endif\r
2900             SelectObject( hdc, hfont_old );\r
2901 \r
2902             fontBitmapSquareSize = squareSize;\r
2903         }\r
2904     }\r
2905 \r
2906     if( hdc != NULL ) {\r
2907         DeleteDC( hdc );\r
2908     }\r
2909 \r
2910     if( hdc_window != NULL ) {\r
2911         ReleaseDC( hwndMain, hdc_window );\r
2912     }\r
2913 }\r
2914 \r
2915 HBITMAP\r
2916 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2917 {\r
2918   char name[128];\r
2919 \r
2920   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2921   if (gameInfo.event &&\r
2922       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2923       strcmp(name, "k80s") == 0) {\r
2924     strcpy(name, "tim");\r
2925   }\r
2926   return LoadBitmap(hinst, name);\r
2927 }\r
2928 \r
2929 \r
2930 /* Insert a color into the program's logical palette\r
2931    structure.  This code assumes the given color is\r
2932    the result of the RGB or PALETTERGB macro, and it\r
2933    knows how those macros work (which is documented).\r
2934 */\r
2935 VOID\r
2936 InsertInPalette(COLORREF color)\r
2937 {\r
2938   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2939 \r
2940   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2941     DisplayFatalError("Too many colors", 0, 1);\r
2942     pLogPal->palNumEntries--;\r
2943     return;\r
2944   }\r
2945 \r
2946   pe->peFlags = (char) 0;\r
2947   pe->peRed = (char) (0xFF & color);\r
2948   pe->peGreen = (char) (0xFF & (color >> 8));\r
2949   pe->peBlue = (char) (0xFF & (color >> 16));\r
2950   return;\r
2951 }\r
2952 \r
2953 \r
2954 VOID\r
2955 InitDrawingColors()\r
2956 {\r
2957   if (pLogPal == NULL) {\r
2958     /* Allocate enough memory for a logical palette with\r
2959      * PALETTESIZE entries and set the size and version fields\r
2960      * of the logical palette structure.\r
2961      */\r
2962     pLogPal = (NPLOGPALETTE)\r
2963       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2964                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2965     pLogPal->palVersion    = 0x300;\r
2966   }\r
2967   pLogPal->palNumEntries = 0;\r
2968 \r
2969   InsertInPalette(lightSquareColor);\r
2970   InsertInPalette(darkSquareColor);\r
2971   InsertInPalette(whitePieceColor);\r
2972   InsertInPalette(blackPieceColor);\r
2973   InsertInPalette(highlightSquareColor);\r
2974   InsertInPalette(premoveHighlightColor);\r
2975 \r
2976   /*  create a logical color palette according the information\r
2977    *  in the LOGPALETTE structure.\r
2978    */\r
2979   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2980 \r
2981   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2982   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2983   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2984   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2985   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2986   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2987 \r
2988   /* [AS] Force rendering of the font-based pieces */\r
2989   if( fontBitmapSquareSize > 0 ) {\r
2990     fontBitmapSquareSize = 0;\r
2991   }\r
2992 }\r
2993 \r
2994 \r
2995 int\r
2996 BoardWidth(int boardSize, int n)\r
2997 { /* [HGM] argument n added to allow different width and height */\r
2998   int lineGap = sizeInfo[boardSize].lineGap;\r
2999 \r
3000   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3001       lineGap = appData.overrideLineGap;\r
3002   }\r
3003 \r
3004   return (n + 1) * lineGap +\r
3005           n * sizeInfo[boardSize].squareSize;\r
3006 }\r
3007 \r
3008 /* Respond to board resize by dragging edge */\r
3009 VOID\r
3010 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3011 {\r
3012   BoardSize newSize = NUM_SIZES - 1;\r
3013   static int recurse = 0;\r
3014   if (IsIconic(hwndMain)) return;\r
3015   if (recurse > 0) return;\r
3016   recurse++;\r
3017   while (newSize > 0) {\r
3018         InitDrawingSizes(newSize, 0);\r
3019         if(newSizeX >= sizeInfo[newSize].cliWidth ||\r
3020            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3021     newSize--;\r
3022   } \r
3023   boardSize = newSize;\r
3024   InitDrawingSizes(boardSize, flags);\r
3025   recurse--;\r
3026 }\r
3027 \r
3028 \r
3029 \r
3030 VOID\r
3031 InitDrawingSizes(BoardSize boardSize, int flags)\r
3032 {\r
3033   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3034   ChessSquare piece;\r
3035   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3036   HDC hdc;\r
3037   SIZE clockSize, messageSize;\r
3038   HFONT oldFont;\r
3039   char buf[MSG_SIZ];\r
3040   char *str;\r
3041   HMENU hmenu = GetMenu(hwndMain);\r
3042   RECT crect, wrect;\r
3043   int offby;\r
3044   LOGBRUSH logbrush;\r
3045 \r
3046   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
3047   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3048 \r
3049   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3050   smallLayout = sizeInfo[boardSize].smallLayout;\r
3051   squareSize = sizeInfo[boardSize].squareSize;\r
3052   lineGap = sizeInfo[boardSize].lineGap;\r
3053   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3054 \r
3055   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3056       lineGap = appData.overrideLineGap;\r
3057   }\r
3058 \r
3059   if (tinyLayout != oldTinyLayout) {\r
3060     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3061     if (tinyLayout) {\r
3062       style &= ~WS_SYSMENU;\r
3063       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3064                  "&Minimize\tCtrl+F4");\r
3065     } else {\r
3066       style |= WS_SYSMENU;\r
3067       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3068     }\r
3069     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3070 \r
3071     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3072       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3073         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3074     }\r
3075     DrawMenuBar(hwndMain);\r
3076   }\r
3077 \r
3078   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3079   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3080 \r
3081   /* Get text area sizes */\r
3082   hdc = GetDC(hwndMain);\r
3083   if (appData.clockMode) {\r
3084     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3085   } else {\r
3086     sprintf(buf, "White");\r
3087   }\r
3088   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3089   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3090   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3091   str = "We only care about the height here";\r
3092   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3093   SelectObject(hdc, oldFont);\r
3094   ReleaseDC(hwndMain, hdc);\r
3095 \r
3096   /* Compute where everything goes */\r
3097   if(first.programLogo || second.programLogo) {\r
3098         /* [HGM] logo: if either logo is on, reserve space for it */\r
3099         logoHeight =  2*clockSize.cy;\r
3100         leftLogoRect.left   = OUTER_MARGIN;\r
3101         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3102         leftLogoRect.top    = OUTER_MARGIN;\r
3103         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3104 \r
3105         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3106         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3107         rightLogoRect.top    = OUTER_MARGIN;\r
3108         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3109 \r
3110 \r
3111     blackRect.left = leftLogoRect.right;\r
3112     blackRect.right = rightLogoRect.left;\r
3113     blackRect.top = OUTER_MARGIN;\r
3114     blackRect.bottom = blackRect.top + clockSize.cy;\r
3115 \r
3116     whiteRect.left = blackRect.left ;\r
3117     whiteRect.right = blackRect.right;\r
3118     whiteRect.top = blackRect.bottom;\r
3119     whiteRect.bottom = leftLogoRect.bottom;\r
3120   } else {\r
3121     whiteRect.left = OUTER_MARGIN;\r
3122     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3123     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3124     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3125 \r
3126     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3127     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3128     blackRect.top = whiteRect.top;\r
3129     blackRect.bottom = whiteRect.bottom;\r
3130   }\r
3131 \r
3132   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3133   if (appData.showButtonBar) {\r
3134     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3135       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3136   } else {\r
3137     messageRect.right = OUTER_MARGIN + boardWidth;\r
3138   }\r
3139   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3140   messageRect.bottom = messageRect.top + messageSize.cy;\r
3141 \r
3142   boardRect.left = OUTER_MARGIN;\r
3143   boardRect.right = boardRect.left + boardWidth;\r
3144   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3145   boardRect.bottom = boardRect.top + boardHeight;\r
3146 \r
3147   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3148   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3149   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3150   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3151     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3152   GetWindowRect(hwndMain, &wrect);\r
3153   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3154                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3155   /* compensate if menu bar wrapped */\r
3156   GetClientRect(hwndMain, &crect);\r
3157   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3158   winHeight += offby;\r
3159   switch (flags) {\r
3160   case WMSZ_TOPLEFT:\r
3161     SetWindowPos(hwndMain, NULL, \r
3162                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3163                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3164     break;\r
3165 \r
3166   case WMSZ_TOPRIGHT:\r
3167   case WMSZ_TOP:\r
3168     SetWindowPos(hwndMain, NULL, \r
3169                  wrect.left, wrect.bottom - winHeight, \r
3170                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3171     break;\r
3172 \r
3173   case WMSZ_BOTTOMLEFT:\r
3174   case WMSZ_LEFT:\r
3175     SetWindowPos(hwndMain, NULL, \r
3176                  wrect.right - winWidth, wrect.top, \r
3177                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3178     break;\r
3179 \r
3180   case WMSZ_BOTTOMRIGHT:\r
3181   case WMSZ_BOTTOM:\r
3182   case WMSZ_RIGHT:\r
3183   default:\r
3184     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3185                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3186     break;\r
3187   }\r
3188 \r
3189   hwndPause = NULL;\r
3190   for (i = 0; i < N_BUTTONS; i++) {\r
3191     if (buttonDesc[i].hwnd != NULL) {\r
3192       DestroyWindow(buttonDesc[i].hwnd);\r
3193       buttonDesc[i].hwnd = NULL;\r
3194     }\r
3195     if (appData.showButtonBar) {\r
3196       buttonDesc[i].hwnd =\r
3197         CreateWindow("BUTTON", buttonDesc[i].label,\r
3198                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3199                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3200                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3201                      (HMENU) buttonDesc[i].id,\r
3202                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3203       if (tinyLayout) {\r
3204         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3205                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3206                     MAKELPARAM(FALSE, 0));\r
3207       }\r
3208       if (buttonDesc[i].id == IDM_Pause)\r
3209         hwndPause = buttonDesc[i].hwnd;\r
3210       buttonDesc[i].wndproc = (WNDPROC)\r
3211         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3212     }\r
3213   }\r
3214   if (gridPen != NULL) DeleteObject(gridPen);\r
3215   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3216   if (premovePen != NULL) DeleteObject(premovePen);\r
3217   if (lineGap != 0) {\r
3218     logbrush.lbStyle = BS_SOLID;\r
3219     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3220     gridPen =\r
3221       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3222                    lineGap, &logbrush, 0, NULL);\r
3223     logbrush.lbColor = highlightSquareColor;\r
3224     highlightPen =\r
3225       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3226                    lineGap, &logbrush, 0, NULL);\r
3227 \r
3228     logbrush.lbColor = premoveHighlightColor; \r
3229     premovePen =\r
3230       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3231                    lineGap, &logbrush, 0, NULL);\r
3232 \r
3233     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3234     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3235       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3236       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3237         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3238       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3239         BOARD_WIDTH * (squareSize + lineGap);\r
3240         lineGap / 2 + (i * (squareSize + lineGap));\r
3241       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3242     }\r
3243     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3244       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3245       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3246         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3247         lineGap / 2 + (i * (squareSize + lineGap));\r
3248       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3249         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3250       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3251     }\r
3252   }\r
3253 \r
3254   /* [HGM] Licensing requirement */\r
3255 #ifdef GOTHIC\r
3256   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3257 #endif\r
3258 #ifdef FALCON\r
3259   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3260 #endif\r
3261   GothicPopUp( "", VariantNormal);\r
3262 \r
3263 \r
3264 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3265   oldBoardSize = boardSize;\r
3266   oldTinyLayout = tinyLayout;\r
3267 \r
3268   /* Load piece bitmaps for this board size */\r
3269   for (i=0; i<=2; i++) {\r
3270     for (piece = WhitePawn;\r
3271          (int) piece < (int) BlackPawn;\r
3272          piece = (ChessSquare) ((int) piece + 1)) {\r
3273       if (pieceBitmap[i][piece] != NULL)\r
3274         DeleteObject(pieceBitmap[i][piece]);\r
3275     }\r
3276   }\r
3277 \r
3278   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3279   // Orthodox Chess pieces\r
3280   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3281   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3282   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3283   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3284   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3285   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3286   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3287   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3288   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3289   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3290   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3291   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3292   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3293   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3294   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3295   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3296     // in Shogi, Hijack the unused Queen for Lance\r
3297     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3298     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3299     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3300   } else {\r
3301     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3302     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3303     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3304   }\r
3305 \r
3306   if(squareSize <= 72 && squareSize >= 33) { \r
3307     /* A & C are available in most sizes now */\r
3308     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3309       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3310       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3311       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3312       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3313       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3314       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3315       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3316       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3317       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3318       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3319       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3320       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3321     } else { // Smirf-like\r
3322       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3323       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3324       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3325     }\r
3326     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3327       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3328       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3329       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3330     } else { // WinBoard standard\r
3331       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3332       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3333       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3334     }\r
3335   }\r
3336 \r
3337 \r
3338   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3339     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3340     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3341     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3342     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3343     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3344     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3345     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3346     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3347     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3348     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3349     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3350     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3351     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3352     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3353     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3354     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3355     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3356     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3357     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3358     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3359     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3360     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3361     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3362     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3363     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3364     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3365     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3366     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3367     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3368     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3369 \r
3370     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3371       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3372       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3373       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3374       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3375       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3376       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3377       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3378       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3379       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3380       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3381       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3382       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3383     } else {\r
3384       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3385       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3386       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3387       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3388       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3389       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3390       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3391       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3392       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3393       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3394       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3395       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3396     }\r
3397 \r
3398   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3399     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3400     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3401     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3402     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3403     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3404     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3405     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3406     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3407     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3408     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3409     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3410     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3411     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3412     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3413   }\r
3414 \r
3415 \r
3416   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3417   /* special Shogi support in this size */\r
3418   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3419       for (piece = WhitePawn;\r
3420            (int) piece < (int) BlackPawn;\r
3421            piece = (ChessSquare) ((int) piece + 1)) {\r
3422         if (pieceBitmap[i][piece] != NULL)\r
3423           DeleteObject(pieceBitmap[i][piece]);\r
3424       }\r
3425     }\r
3426   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3427   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3428   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3429   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3430   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3431   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3432   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3433   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3434   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3435   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3436   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3437   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3438   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3439   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3440   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3441   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3442   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3443   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3444   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3445   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3446   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3447   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3448   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3449   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3450   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3451   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3452   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3453   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3454   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3455   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3456   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3457   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3458   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3459   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3460   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3461   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3462   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3463   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3464   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3465   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3466   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3467   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3468   minorSize = 0;\r
3469   }\r
3470 }\r
3471 \r
3472 HBITMAP\r
3473 PieceBitmap(ChessSquare p, int kind)\r
3474 {\r
3475   if ((int) p >= (int) BlackPawn)\r
3476     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3477 \r
3478   return pieceBitmap[kind][(int) p];\r
3479 }\r
3480 \r
3481 /***************************************************************/\r
3482 \r
3483 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3484 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3485 /*\r
3486 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3487 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3488 */\r
3489 \r
3490 VOID\r
3491 SquareToPos(int row, int column, int * x, int * y)\r
3492 {\r
3493   if (flipView) {\r
3494     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3495     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3496   } else {\r
3497     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3498     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3499   }\r
3500 }\r
3501 \r
3502 VOID\r
3503 DrawCoordsOnDC(HDC hdc)\r
3504 {\r
3505   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
3506   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
3507   char str[2] = { NULLCHAR, NULLCHAR };\r
3508   int oldMode, oldAlign, x, y, start, i;\r
3509   HFONT oldFont;\r
3510   HBRUSH oldBrush;\r
3511 \r
3512   if (!appData.showCoords)\r
3513     return;\r
3514 \r
3515   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3516 \r
3517   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3518   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3519   oldAlign = GetTextAlign(hdc);\r
3520   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3521 \r
3522   y = boardRect.top + lineGap;\r
3523   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3524 \r
3525   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3526   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3527     str[0] = files[start + i];\r
3528     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3529     y += squareSize + lineGap;\r
3530   }\r
3531 \r
3532   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3533 \r
3534   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3535   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3536     str[0] = ranks[start + i];\r
3537     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3538     x += squareSize + lineGap;\r
3539   }    \r
3540 \r
3541   SelectObject(hdc, oldBrush);\r
3542   SetBkMode(hdc, oldMode);\r
3543   SetTextAlign(hdc, oldAlign);\r
3544   SelectObject(hdc, oldFont);\r
3545 }\r
3546 \r
3547 VOID\r
3548 DrawGridOnDC(HDC hdc)\r
3549 {\r
3550   HPEN oldPen;\r
3551  \r
3552   if (lineGap != 0) {\r
3553     oldPen = SelectObject(hdc, gridPen);\r
3554     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3555     SelectObject(hdc, oldPen);\r
3556   }\r
3557 }\r
3558 \r
3559 #define HIGHLIGHT_PEN 0\r
3560 #define PREMOVE_PEN   1\r
3561 \r
3562 VOID\r
3563 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3564 {\r
3565   int x1, y1;\r
3566   HPEN oldPen, hPen;\r
3567   if (lineGap == 0) return;\r
3568   if (flipView) {\r
3569     x1 = boardRect.left +\r
3570       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3571     y1 = boardRect.top +\r
3572       lineGap/2 + y * (squareSize + lineGap);\r
3573   } else {\r
3574     x1 = boardRect.left +\r
3575       lineGap/2 + x * (squareSize + lineGap);\r
3576     y1 = boardRect.top +\r
3577       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3578   }\r
3579   hPen = pen ? premovePen : highlightPen;\r
3580   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3581   MoveToEx(hdc, x1, y1, NULL);\r
3582   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3583   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3584   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3585   LineTo(hdc, x1, y1);\r
3586   SelectObject(hdc, oldPen);\r
3587 }\r
3588 \r
3589 VOID\r
3590 DrawHighlightsOnDC(HDC hdc)\r
3591 {\r
3592   int i;\r
3593   for (i=0; i<2; i++) {\r
3594     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3595       DrawHighlightOnDC(hdc, TRUE,\r
3596                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3597                         HIGHLIGHT_PEN);\r
3598   }\r
3599   for (i=0; i<2; i++) {\r
3600     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3601         premoveHighlightInfo.sq[i].y >= 0) {\r
3602         DrawHighlightOnDC(hdc, TRUE,\r
3603                           premoveHighlightInfo.sq[i].x, \r
3604                           premoveHighlightInfo.sq[i].y,\r
3605                           PREMOVE_PEN);\r
3606     }\r
3607   }\r
3608 }\r
3609 \r
3610 /* Note: sqcolor is used only in monoMode */\r
3611 /* Note that this code is largely duplicated in woptions.c,\r
3612    function DrawSampleSquare, so that needs to be updated too */\r
3613 VOID\r
3614 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3615 {\r
3616   HBITMAP oldBitmap;\r
3617   HBRUSH oldBrush;\r
3618   int tmpSize;\r
3619 \r
3620   if (appData.blindfold) return;\r
3621 \r
3622   /* [AS] Use font-based pieces if needed */\r
3623   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3624     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3625     CreatePiecesFromFont();\r
3626 \r
3627     if( fontBitmapSquareSize == squareSize ) {\r
3628         int index = TranslatePieceToFontPiece(piece);\r
3629 \r
3630         SelectObject( tmphdc, hPieceMask[ index ] );\r
3631 \r
3632         BitBlt( hdc,\r
3633             x, y,\r
3634             squareSize, squareSize,\r
3635             tmphdc,\r
3636             0, 0,\r
3637             SRCAND );\r
3638 \r
3639         SelectObject( tmphdc, hPieceFace[ index ] );\r
3640 \r
3641         BitBlt( hdc,\r
3642             x, y,\r
3643             squareSize, squareSize,\r
3644             tmphdc,\r
3645             0, 0,\r
3646             SRCPAINT );\r
3647 \r
3648         return;\r
3649     }\r
3650   }\r
3651 \r
3652   if (appData.monoMode) {\r
3653     SelectObject(tmphdc, PieceBitmap(piece, \r
3654       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3655     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3656            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3657   } else {\r
3658     tmpSize = squareSize;\r
3659     if(minorSize &&\r
3660         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3661          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3662       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3663       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3664       x += (squareSize - minorSize)>>1;\r
3665       y += squareSize - minorSize - 2;\r
3666       tmpSize = minorSize;\r
3667     }\r
3668     if (color || appData.allWhite ) {\r
3669       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3670       if( color )\r
3671               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3672       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3673       if(appData.upsideDown && color==flipView)\r
3674         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3675       else\r
3676         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3677 #if 0\r
3678       /* Use black piece color for outline of white pieces */\r
3679       /* Not sure this looks really good (though xboard does it).\r
3680          Maybe better to have another selectable color, default black */\r
3681       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3682       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3683       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3684 #else\r
3685       /* Use black for outline of white pieces */\r
3686       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3687       if(appData.upsideDown && color==flipView)\r
3688         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3689       else\r
3690         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3691 #endif\r
3692     } else {\r
3693 #if 0\r
3694       /* Use white piece color for details of black pieces */\r
3695       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3696          WHITE_PIECE ones aren't always the right shape. */\r
3697       /* Not sure this looks really good (though xboard does it).\r
3698          Maybe better to have another selectable color, default medium gray? */\r
3699       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3700       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3701       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3702       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3703       SelectObject(hdc, blackPieceBrush);\r
3704       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3705 #else\r
3706       /* Use square color for details of black pieces */\r
3707       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3708       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3709       if(appData.upsideDown && !flipView)\r
3710         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3711       else\r
3712         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3713 #endif\r
3714     }\r
3715     SelectObject(hdc, oldBrush);\r
3716     SelectObject(tmphdc, oldBitmap);\r
3717   }\r
3718 }\r
3719 \r
3720 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3721 int GetBackTextureMode( int algo )\r
3722 {\r
3723     int result = BACK_TEXTURE_MODE_DISABLED;\r
3724 \r
3725     switch( algo ) \r
3726     {\r
3727         case BACK_TEXTURE_MODE_PLAIN:\r
3728             result = 1; /* Always use identity map */\r
3729             break;\r
3730         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3731             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3732             break;\r
3733     }\r
3734 \r
3735     return result;\r
3736 }\r
3737 \r
3738 /* \r
3739     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3740     to handle redraws cleanly (as random numbers would always be different).\r
3741 */\r
3742 VOID RebuildTextureSquareInfo()\r
3743 {\r
3744     BITMAP bi;\r
3745     int lite_w = 0;\r
3746     int lite_h = 0;\r
3747     int dark_w = 0;\r
3748     int dark_h = 0;\r
3749     int row;\r
3750     int col;\r
3751 \r
3752     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3753 \r
3754     if( liteBackTexture != NULL ) {\r
3755         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3756             lite_w = bi.bmWidth;\r
3757             lite_h = bi.bmHeight;\r
3758         }\r
3759     }\r
3760 \r
3761     if( darkBackTexture != NULL ) {\r
3762         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3763             dark_w = bi.bmWidth;\r
3764             dark_h = bi.bmHeight;\r
3765         }\r
3766     }\r
3767 \r
3768     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3769         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3770             if( (col + row) & 1 ) {\r
3771                 /* Lite square */\r
3772                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3773                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3774                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3775                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3776                 }\r
3777             }\r
3778             else {\r
3779                 /* Dark square */\r
3780                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3781                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3782                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3783                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3784                 }\r
3785             }\r
3786         }\r
3787     }\r
3788 }\r
3789 \r
3790 /* [AS] Arrow highlighting support */\r
3791 \r
3792 static int A_WIDTH = 5; /* Width of arrow body */\r
3793 \r
3794 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3795 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3796 \r
3797 static double Sqr( double x )\r
3798 {\r
3799     return x*x;\r
3800 }\r
3801 \r
3802 static int Round( double x )\r
3803 {\r
3804     return (int) (x + 0.5);\r
3805 }\r
3806 \r
3807 /* Draw an arrow between two points using current settings */\r
3808 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3809 {\r
3810     POINT arrow[7];\r
3811     double dx, dy, j, k, x, y;\r
3812 \r
3813     if( d_x == s_x ) {\r
3814         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3815 \r
3816         arrow[0].x = s_x + A_WIDTH;\r
3817         arrow[0].y = s_y;\r
3818 \r
3819         arrow[1].x = s_x + A_WIDTH;\r
3820         arrow[1].y = d_y - h;\r
3821 \r
3822         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3823         arrow[2].y = d_y - h;\r
3824 \r
3825         arrow[3].x = d_x;\r
3826         arrow[3].y = d_y;\r
3827 \r
3828         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3829         arrow[4].y = d_y - h;\r
3830 \r
3831         arrow[5].x = s_x - A_WIDTH;\r
3832         arrow[5].y = d_y - h;\r
3833 \r
3834         arrow[6].x = s_x - A_WIDTH;\r
3835         arrow[6].y = s_y;\r
3836     }\r
3837     else if( d_y == s_y ) {\r
3838         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3839 \r
3840         arrow[0].x = s_x;\r
3841         arrow[0].y = s_y + A_WIDTH;\r
3842 \r
3843         arrow[1].x = d_x - w;\r
3844         arrow[1].y = s_y + A_WIDTH;\r
3845 \r
3846         arrow[2].x = d_x - w;\r
3847         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3848 \r
3849         arrow[3].x = d_x;\r
3850         arrow[3].y = d_y;\r
3851 \r
3852         arrow[4].x = d_x - w;\r
3853         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3854 \r
3855         arrow[5].x = d_x - w;\r
3856         arrow[5].y = s_y - A_WIDTH;\r
3857 \r
3858         arrow[6].x = s_x;\r
3859         arrow[6].y = s_y - A_WIDTH;\r
3860     }\r
3861     else {\r
3862         /* [AS] Needed a lot of paper for this! :-) */\r
3863         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3864         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3865   \r
3866         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3867 \r
3868         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3869 \r
3870         x = s_x;\r
3871         y = s_y;\r
3872 \r
3873         arrow[0].x = Round(x - j);\r
3874         arrow[0].y = Round(y + j*dx);\r
3875 \r
3876         arrow[1].x = Round(x + j);\r
3877         arrow[1].y = Round(y - j*dx);\r
3878 \r
3879         if( d_x > s_x ) {\r
3880             x = (double) d_x - k;\r
3881             y = (double) d_y - k*dy;\r
3882         }\r
3883         else {\r
3884             x = (double) d_x + k;\r
3885             y = (double) d_y + k*dy;\r
3886         }\r
3887 \r
3888         arrow[2].x = Round(x + j);\r
3889         arrow[2].y = Round(y - j*dx);\r
3890 \r
3891         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3892         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3893 \r
3894         arrow[4].x = d_x;\r
3895         arrow[4].y = d_y;\r
3896 \r
3897         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3898         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3899 \r
3900         arrow[6].x = Round(x - j);\r
3901         arrow[6].y = Round(y + j*dx);\r
3902     }\r
3903 \r
3904     Polygon( hdc, arrow, 7 );\r
3905 }\r
3906 \r
3907 /* [AS] Draw an arrow between two squares */\r
3908 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3909 {\r
3910     int s_x, s_y, d_x, d_y;\r
3911     HPEN hpen;\r
3912     HPEN holdpen;\r
3913     HBRUSH hbrush;\r
3914     HBRUSH holdbrush;\r
3915     LOGBRUSH stLB;\r
3916 \r
3917     if( s_col == d_col && s_row == d_row ) {\r
3918         return;\r
3919     }\r
3920 \r
3921     /* Get source and destination points */\r
3922     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3923     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3924 \r
3925     if( d_y > s_y ) {\r
3926         d_y += squareSize / 4;\r
3927     }\r
3928     else if( d_y < s_y ) {\r
3929         d_y += 3 * squareSize / 4;\r
3930     }\r
3931     else {\r
3932         d_y += squareSize / 2;\r
3933     }\r
3934 \r
3935     if( d_x > s_x ) {\r
3936         d_x += squareSize / 4;\r
3937     }\r
3938     else if( d_x < s_x ) {\r
3939         d_x += 3 * squareSize / 4;\r
3940     }\r
3941     else {\r
3942         d_x += squareSize / 2;\r
3943     }\r
3944 \r
3945     s_x += squareSize / 2;\r
3946     s_y += squareSize / 2;\r
3947 \r
3948     /* Adjust width */\r
3949     A_WIDTH = squareSize / 14;\r
3950 \r
3951     /* Draw */\r
3952     stLB.lbStyle = BS_SOLID;\r
3953     stLB.lbColor = appData.highlightArrowColor;\r
3954     stLB.lbHatch = 0;\r
3955 \r
3956     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3957     holdpen = SelectObject( hdc, hpen );\r
3958     hbrush = CreateBrushIndirect( &stLB );\r
3959     holdbrush = SelectObject( hdc, hbrush );\r
3960 \r
3961     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3962 \r
3963     SelectObject( hdc, holdpen );\r
3964     SelectObject( hdc, holdbrush );\r
3965     DeleteObject( hpen );\r
3966     DeleteObject( hbrush );\r
3967 }\r
3968 \r
3969 BOOL HasHighlightInfo()\r
3970 {\r
3971     BOOL result = FALSE;\r
3972 \r
3973     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3974         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3975     {\r
3976         result = TRUE;\r
3977     }\r
3978 \r
3979     return result;\r
3980 }\r
3981 \r
3982 BOOL IsDrawArrowEnabled()\r
3983 {\r
3984     BOOL result = FALSE;\r
3985 \r
3986     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3987         result = TRUE;\r
3988     }\r
3989 \r
3990     return result;\r
3991 }\r
3992 \r
3993 VOID DrawArrowHighlight( HDC hdc )\r
3994 {\r
3995     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3996         DrawArrowBetweenSquares( hdc,\r
3997             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3998             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3999     }\r
4000 }\r
4001 \r
4002 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4003 {\r
4004     HRGN result = NULL;\r
4005 \r
4006     if( HasHighlightInfo() ) {\r
4007         int x1, y1, x2, y2;\r
4008         int sx, sy, dx, dy;\r
4009 \r
4010         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4011         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4012 \r
4013         sx = MIN( x1, x2 );\r
4014         sy = MIN( y1, y2 );\r
4015         dx = MAX( x1, x2 ) + squareSize;\r
4016         dy = MAX( y1, y2 ) + squareSize;\r
4017 \r
4018         result = CreateRectRgn( sx, sy, dx, dy );\r
4019     }\r
4020 \r
4021     return result;\r
4022 }\r
4023 \r
4024 /*\r
4025     Warning: this function modifies the behavior of several other functions. \r
4026     \r
4027     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4028     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4029     repaint is scattered all over the place, which is not good for features such as\r
4030     "arrow highlighting" that require a full repaint of the board.\r
4031 \r
4032     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4033     user interaction, when speed is not so important) but especially to avoid errors\r
4034     in the displayed graphics.\r
4035 \r
4036     In such patched places, I always try refer to this function so there is a single\r
4037     place to maintain knowledge.\r
4038     \r
4039     To restore the original behavior, just return FALSE unconditionally.\r
4040 */\r
4041 BOOL IsFullRepaintPreferrable()\r
4042 {\r
4043     BOOL result = FALSE;\r
4044 \r
4045     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4046         /* Arrow may appear on the board */\r
4047         result = TRUE;\r
4048     }\r
4049 \r
4050     return result;\r
4051 }\r
4052 \r
4053 /* \r
4054     This function is called by DrawPosition to know whether a full repaint must\r
4055     be forced or not.\r
4056 \r
4057     Only DrawPosition may directly call this function, which makes use of \r
4058     some state information. Other function should call DrawPosition specifying \r
4059     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4060 */\r
4061 BOOL DrawPositionNeedsFullRepaint()\r
4062 {\r
4063     BOOL result = FALSE;\r
4064 \r
4065     /* \r
4066         Probably a slightly better policy would be to trigger a full repaint\r
4067         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4068         but animation is fast enough that it's difficult to notice.\r
4069     */\r
4070     if( animInfo.piece == EmptySquare ) {\r
4071         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4072             result = TRUE;\r
4073         }\r
4074     }\r
4075 \r
4076     return result;\r
4077 }\r
4078 \r
4079 VOID\r
4080 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4081 {\r
4082   int row, column, x, y, square_color, piece_color;\r
4083   ChessSquare piece;\r
4084   HBRUSH oldBrush;\r
4085   HDC texture_hdc = NULL;\r
4086 \r
4087   /* [AS] Initialize background textures if needed */\r
4088   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4089       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4090       if( backTextureSquareSize != squareSize \r
4091        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4092           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4093           backTextureSquareSize = squareSize;\r
4094           RebuildTextureSquareInfo();\r
4095       }\r
4096 \r
4097       texture_hdc = CreateCompatibleDC( hdc );\r
4098   }\r
4099 \r
4100   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4101     for (column = 0; column < BOARD_WIDTH; column++) {\r
4102   \r
4103       SquareToPos(row, column, &x, &y);\r
4104 \r
4105       piece = board[row][column];\r
4106 \r
4107       square_color = ((column + row) % 2) == 1;\r
4108       if( gameInfo.variant == VariantXiangqi ) {\r
4109           square_color = !InPalace(row, column);\r
4110           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4111           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4112       }\r
4113       piece_color = (int) piece < (int) BlackPawn;\r
4114 \r
4115 \r
4116       /* [HGM] holdings file: light square or black */\r
4117       if(column == BOARD_LEFT-2) {\r
4118             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4119                 square_color = 1;\r
4120             else {\r
4121                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4122                 continue;\r
4123             }\r
4124       } else\r
4125       if(column == BOARD_RGHT + 1 ) {\r
4126             if( row < gameInfo.holdingsSize )\r
4127                 square_color = 1;\r
4128             else {\r
4129                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4130                 continue;\r
4131             }\r
4132       }\r
4133       if(column == BOARD_LEFT-1 ) /* left align */\r
4134             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4135       else if( column == BOARD_RGHT) /* right align */\r
4136             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4137       else\r
4138       if (appData.monoMode) {\r
4139         if (piece == EmptySquare) {\r
4140           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4141                  square_color ? WHITENESS : BLACKNESS);\r
4142         } else {\r
4143           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4144         }\r
4145       } \r
4146       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4147           /* [AS] Draw the square using a texture bitmap */\r
4148           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4149           int r = row, c = column; // [HGM] do not flip board in flipView\r
4150           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4151 \r
4152           DrawTile( x, y, \r
4153               squareSize, squareSize, \r
4154               hdc, \r
4155               texture_hdc,\r
4156               backTextureSquareInfo[r][c].mode,\r
4157               backTextureSquareInfo[r][c].x,\r
4158               backTextureSquareInfo[r][c].y );\r
4159 \r
4160           SelectObject( texture_hdc, hbm );\r
4161 \r
4162           if (piece != EmptySquare) {\r
4163               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4164           }\r
4165       }\r
4166       else {\r
4167         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4168 \r
4169         oldBrush = SelectObject(hdc, brush );\r
4170         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4171         SelectObject(hdc, oldBrush);\r
4172         if (piece != EmptySquare)\r
4173           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4174       }\r
4175     }\r
4176   }\r
4177 \r
4178   if( texture_hdc != NULL ) {\r
4179     DeleteDC( texture_hdc );\r
4180   }\r
4181 }\r
4182 \r
4183 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4184 void fputDW(FILE *f, int x)\r
4185 {\r
4186         fputc(x     & 255, f);\r
4187         fputc(x>>8  & 255, f);\r
4188         fputc(x>>16 & 255, f);\r
4189         fputc(x>>24 & 255, f);\r
4190 }\r
4191 \r
4192 #define MAX_CLIPS 200   /* more than enough */\r
4193 \r
4194 VOID\r
4195 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4196 {\r
4197   HBITMAP bufferBitmap;\r
4198   BITMAP bi;\r
4199   RECT Rect;\r
4200   HDC tmphdc;\r
4201   HBITMAP hbm;\r
4202   int w = 100, h = 50;\r
4203 \r
4204   if(cps->programLogo == NULL) return;\r
4205 //  GetClientRect(hwndMain, &Rect);\r
4206 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4207 //                                      Rect.bottom-Rect.top+1);\r
4208   tmphdc = CreateCompatibleDC(hdc);\r
4209   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4210   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4211             w = bi.bmWidth;\r
4212             h = bi.bmHeight;\r
4213   }\r
4214   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4215                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4216   SelectObject(tmphdc, hbm);\r
4217   DeleteDC(tmphdc);\r
4218 }\r
4219 \r
4220 VOID\r
4221 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4222 {\r
4223   static Board lastReq, lastDrawn;\r
4224   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4225   static int lastDrawnFlipView = 0;\r
4226   static int lastReqValid = 0, lastDrawnValid = 0;\r
4227   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4228   HDC tmphdc;\r
4229   HDC hdcmem;\r
4230   HBITMAP bufferBitmap;\r
4231   HBITMAP oldBitmap;\r
4232   RECT Rect;\r
4233   HRGN clips[MAX_CLIPS];\r
4234   ChessSquare dragged_piece = EmptySquare;\r
4235 \r
4236   /* I'm undecided on this - this function figures out whether a full\r
4237    * repaint is necessary on its own, so there's no real reason to have the\r
4238    * caller tell it that.  I think this can safely be set to FALSE - but\r
4239    * if we trust the callers not to request full repaints unnessesarily, then\r
4240    * we could skip some clipping work.  In other words, only request a full\r
4241    * redraw when the majority of pieces have changed positions (ie. flip, \r
4242    * gamestart and similar)  --Hawk\r
4243    */\r
4244   Boolean fullrepaint = repaint;\r
4245 \r
4246   if( DrawPositionNeedsFullRepaint() ) {\r
4247       fullrepaint = TRUE;\r
4248   }\r
4249 \r
4250 #if 0\r
4251   if( fullrepaint ) {\r
4252       static int repaint_count = 0;\r
4253       char buf[128];\r
4254 \r
4255       repaint_count++;\r
4256       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4257       OutputDebugString( buf );\r
4258   }\r
4259 #endif\r
4260 \r
4261   if (board == NULL) {\r
4262     if (!lastReqValid) {\r
4263       return;\r
4264     }\r
4265     board = lastReq;\r
4266   } else {\r
4267     CopyBoard(lastReq, board);\r
4268     lastReqValid = 1;\r
4269   }\r
4270 \r
4271   if (doingSizing) {\r
4272     return;\r
4273   }\r
4274 \r
4275   if (IsIconic(hwndMain)) {\r
4276     return;\r
4277   }\r
4278 \r
4279   if (hdc == NULL) {\r
4280     hdc = GetDC(hwndMain);\r
4281     if (!appData.monoMode) {\r
4282       SelectPalette(hdc, hPal, FALSE);\r
4283       RealizePalette(hdc);\r
4284     }\r
4285     releaseDC = TRUE;\r
4286   } else {\r
4287     releaseDC = FALSE;\r
4288   }\r
4289 \r
4290 #if 0\r
4291   fprintf(debugFP, "*******************************\n"\r
4292                    "repaint = %s\n"\r
4293                    "dragInfo.from (%d,%d)\n"\r
4294                    "dragInfo.start (%d,%d)\n"\r
4295                    "dragInfo.pos (%d,%d)\n"\r
4296                    "dragInfo.lastpos (%d,%d)\n", \r
4297                     repaint ? "TRUE" : "FALSE",\r
4298                     dragInfo.from.x, dragInfo.from.y, \r
4299                     dragInfo.start.x, dragInfo.start.y,\r
4300                     dragInfo.pos.x, dragInfo.pos.y,\r
4301                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4302   fprintf(debugFP, "prev:  ");\r
4303   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4304     for (column = 0; column < BOARD_WIDTH; column++) {\r
4305       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4306     }\r
4307   }\r
4308   fprintf(debugFP, "\n");\r
4309   fprintf(debugFP, "board: ");\r
4310   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4311     for (column = 0; column < BOARD_WIDTH; column++) {\r
4312       fprintf(debugFP, "%d ", board[row][column]);\r
4313     }\r
4314   }\r
4315   fprintf(debugFP, "\n");\r
4316   fflush(debugFP);\r
4317 #endif\r
4318 \r
4319   /* Create some work-DCs */\r
4320   hdcmem = CreateCompatibleDC(hdc);\r
4321   tmphdc = CreateCompatibleDC(hdc);\r
4322 \r
4323   /* If dragging is in progress, we temporarely remove the piece */\r
4324   /* [HGM] or temporarily decrease count if stacked              */\r
4325   /*       !! Moved to before board compare !!                   */\r
4326   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4327     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4328     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4329             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4330         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4331     } else \r
4332     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4333             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4334         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4335     } else \r
4336         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4337   }\r
4338 \r
4339   /* Figure out which squares need updating by comparing the \r
4340    * newest board with the last drawn board and checking if\r
4341    * flipping has changed.\r
4342    */\r
4343   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4344     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4345       for (column = 0; column < BOARD_WIDTH; column++) {\r
4346         if (lastDrawn[row][column] != board[row][column]) {\r
4347           SquareToPos(row, column, &x, &y);\r
4348           clips[num_clips++] =\r
4349             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4350         }\r
4351       }\r
4352     }\r
4353     for (i=0; i<2; i++) {\r
4354       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4355           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4356         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4357             lastDrawnHighlight.sq[i].y >= 0) {\r
4358           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4359                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4360           clips[num_clips++] =\r
4361             CreateRectRgn(x - lineGap, y - lineGap, \r
4362                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4363         }\r
4364         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4365           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4366           clips[num_clips++] =\r
4367             CreateRectRgn(x - lineGap, y - lineGap, \r
4368                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4369         }\r
4370       }\r
4371     }\r
4372     for (i=0; i<2; i++) {\r
4373       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4374           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4375         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4376             lastDrawnPremove.sq[i].y >= 0) {\r
4377           SquareToPos(lastDrawnPremove.sq[i].y,\r
4378                       lastDrawnPremove.sq[i].x, &x, &y);\r
4379           clips[num_clips++] =\r
4380             CreateRectRgn(x - lineGap, y - lineGap, \r
4381                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4382         }\r
4383         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4384             premoveHighlightInfo.sq[i].y >= 0) {\r
4385           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4386                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x - lineGap, y - lineGap, \r
4389                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4390         }\r
4391       }\r
4392     }\r
4393   } else {\r
4394     fullrepaint = TRUE;\r
4395   }\r
4396 \r
4397   /* Create a buffer bitmap - this is the actual bitmap\r
4398    * being written to.  When all the work is done, we can\r
4399    * copy it to the real DC (the screen).  This avoids\r
4400    * the problems with flickering.\r
4401    */\r
4402   GetClientRect(hwndMain, &Rect);\r
4403   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4404                                         Rect.bottom-Rect.top+1);\r
4405   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4406   if (!appData.monoMode) {\r
4407     SelectPalette(hdcmem, hPal, FALSE);\r
4408   }\r
4409 \r
4410   /* Create clips for dragging */\r
4411   if (!fullrepaint) {\r
4412     if (dragInfo.from.x >= 0) {\r
4413       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4414       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4415     }\r
4416     if (dragInfo.start.x >= 0) {\r
4417       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4418       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4419     }\r
4420     if (dragInfo.pos.x >= 0) {\r
4421       x = dragInfo.pos.x - squareSize / 2;\r
4422       y = dragInfo.pos.y - squareSize / 2;\r
4423       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4424     }\r
4425     if (dragInfo.lastpos.x >= 0) {\r
4426       x = dragInfo.lastpos.x - squareSize / 2;\r
4427       y = dragInfo.lastpos.y - squareSize / 2;\r
4428       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4429     }\r
4430   }\r
4431 \r
4432   /* Are we animating a move?  \r
4433    * If so, \r
4434    *   - remove the piece from the board (temporarely)\r
4435    *   - calculate the clipping region\r
4436    */\r
4437   if (!fullrepaint) {\r
4438     if (animInfo.piece != EmptySquare) {\r
4439       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4440       x = boardRect.left + animInfo.lastpos.x;\r
4441       y = boardRect.top + animInfo.lastpos.y;\r
4442       x2 = boardRect.left + animInfo.pos.x;\r
4443       y2 = boardRect.top + animInfo.pos.y;\r
4444       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4445       /* Slight kludge.  The real problem is that after AnimateMove is\r
4446          done, the position on the screen does not match lastDrawn.\r
4447          This currently causes trouble only on e.p. captures in\r
4448          atomic, where the piece moves to an empty square and then\r
4449          explodes.  The old and new positions both had an empty square\r
4450          at the destination, but animation has drawn a piece there and\r
4451          we have to remember to erase it. */\r
4452       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4453     }\r
4454   }\r
4455 \r
4456   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4457   if (num_clips == 0)\r
4458     fullrepaint = TRUE;\r
4459 \r
4460   /* Set clipping on the memory DC */\r
4461   if (!fullrepaint) {\r
4462     SelectClipRgn(hdcmem, clips[0]);\r
4463     for (x = 1; x < num_clips; x++) {\r
4464       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4465         abort();  // this should never ever happen!\r
4466     }\r
4467   }\r
4468 \r
4469   /* Do all the drawing to the memory DC */\r
4470   DrawGridOnDC(hdcmem);\r
4471   DrawHighlightsOnDC(hdcmem);\r
4472   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4473 \r
4474   if(logoHeight) {\r
4475         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4476         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4477   }\r
4478 \r
4479   if( appData.highlightMoveWithArrow ) {\r
4480     DrawArrowHighlight(hdcmem);\r
4481   }\r
4482 \r
4483   DrawCoordsOnDC(hdcmem);\r
4484 \r
4485   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4486                  /* to make sure lastDrawn contains what is actually drawn */\r
4487 \r
4488   /* Put the dragged piece back into place and draw it (out of place!) */\r
4489     if (dragged_piece != EmptySquare) {\r
4490     /* [HGM] or restack */\r
4491     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4492                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4493     else\r
4494     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4495                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4496     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4497     x = dragInfo.pos.x - squareSize / 2;\r
4498     y = dragInfo.pos.y - squareSize / 2;\r
4499     DrawPieceOnDC(hdcmem, dragged_piece,\r
4500                   ((int) dragged_piece < (int) BlackPawn), \r
4501                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4502   }   \r
4503   \r
4504   /* Put the animated piece back into place and draw it */\r
4505   if (animInfo.piece != EmptySquare) {\r
4506     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4507     x = boardRect.left + animInfo.pos.x;\r
4508     y = boardRect.top + animInfo.pos.y;\r
4509     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4510                   ((int) animInfo.piece < (int) BlackPawn),\r
4511                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4512   }\r
4513 \r
4514   /* Release the bufferBitmap by selecting in the old bitmap \r
4515    * and delete the memory DC\r
4516    */\r
4517   SelectObject(hdcmem, oldBitmap);\r
4518   DeleteDC(hdcmem);\r
4519 \r
4520   /* Set clipping on the target DC */\r
4521   if (!fullrepaint) {\r
4522     SelectClipRgn(hdc, clips[0]);\r
4523     for (x = 1; x < num_clips; x++) {\r
4524       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4525         abort();   // this should never ever happen!\r
4526     } \r
4527   }\r
4528 \r
4529   /* Copy the new bitmap onto the screen in one go.\r
4530    * This way we avoid any flickering\r
4531    */\r
4532   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4533   BitBlt(hdc, boardRect.left, boardRect.top,\r
4534          boardRect.right - boardRect.left,\r
4535          boardRect.bottom - boardRect.top,\r
4536          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4537   if(saveDiagFlag) { \r
4538     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4539     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4540 \r
4541     GetObject(bufferBitmap, sizeof(b), &b);\r
4542     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4543         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4544         bih.biWidth = b.bmWidth;\r
4545         bih.biHeight = b.bmHeight;\r
4546         bih.biPlanes = 1;\r
4547         bih.biBitCount = b.bmBitsPixel;\r
4548         bih.biCompression = 0;\r
4549         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4550         bih.biXPelsPerMeter = 0;\r
4551         bih.biYPelsPerMeter = 0;\r
4552         bih.biClrUsed = 0;\r
4553         bih.biClrImportant = 0;\r
4554 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4555 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4556         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4557 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4558 \r
4559 #if 1\r
4560         wb = b.bmWidthBytes;\r
4561         // count colors\r
4562         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4563                 int k = ((int*) pData)[i];\r
4564                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4565                 if(j >= 16) break;\r
4566                 color[j] = k;\r
4567                 if(j >= nrColors) nrColors = j+1;\r
4568         }\r
4569         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4570                 INT p = 0;\r
4571                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4572                     for(w=0; w<(wb>>2); w+=2) {\r
4573                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4574                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4575                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4576                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4577                         pData[p++] = m | j<<4;\r
4578                     }\r
4579                     while(p&3) pData[p++] = 0;\r
4580                 }\r
4581                 fac = 3;\r
4582                 wb = (wb+31>>5)<<2;\r
4583         }\r
4584         // write BITMAPFILEHEADER\r
4585         fprintf(diagFile, "BM");\r
4586         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4587         fputDW(diagFile, 0);\r
4588         fputDW(diagFile, 0x36 + (fac?64:0));\r
4589         // write BITMAPINFOHEADER\r
4590         fputDW(diagFile, 40);\r
4591         fputDW(diagFile, b.bmWidth);\r
4592         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4593         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4594         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4595         fputDW(diagFile, 0);\r
4596         fputDW(diagFile, 0);\r
4597         fputDW(diagFile, 0);\r
4598         fputDW(diagFile, 0);\r
4599         fputDW(diagFile, 0);\r
4600         fputDW(diagFile, 0);\r
4601         // write color table\r
4602         if(fac)\r
4603         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4604         // write bitmap data\r
4605         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4606                 fputc(pData[i], diagFile);\r
4607 #endif\r
4608      }\r
4609   }\r
4610 \r
4611   SelectObject(tmphdc, oldBitmap);\r
4612 \r
4613   /* Massive cleanup */\r
4614   for (x = 0; x < num_clips; x++)\r
4615     DeleteObject(clips[x]);\r
4616 \r
4617   DeleteDC(tmphdc);\r
4618   DeleteObject(bufferBitmap);\r
4619 \r
4620   if (releaseDC) \r
4621     ReleaseDC(hwndMain, hdc);\r
4622   \r
4623   if (lastDrawnFlipView != flipView) {\r
4624     if (flipView)\r
4625       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4626     else\r
4627       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4628   }\r
4629 \r
4630 /*  CopyBoard(lastDrawn, board);*/\r
4631   lastDrawnHighlight = highlightInfo;\r
4632   lastDrawnPremove   = premoveHighlightInfo;\r
4633   lastDrawnFlipView = flipView;\r
4634   lastDrawnValid = 1;\r
4635 }\r
4636 \r
4637 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4638 int\r
4639 SaveDiagram(f)\r
4640      FILE *f;\r
4641 {\r
4642     time_t tm;\r
4643     char *fen;\r
4644 \r
4645     saveDiagFlag = 1; diagFile = f;\r
4646     HDCDrawPosition(NULL, TRUE, NULL);\r
4647 \r
4648     saveDiagFlag = 0;\r
4649 \r
4650 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4651     \r
4652     fclose(f);\r
4653     return TRUE;\r
4654 }\r
4655 \r
4656 \r
4657 /*---------------------------------------------------------------------------*\\r
4658 | CLIENT PAINT PROCEDURE\r
4659 |   This is the main event-handler for the WM_PAINT message.\r
4660 |\r
4661 \*---------------------------------------------------------------------------*/\r
4662 VOID\r
4663 PaintProc(HWND hwnd)\r
4664 {\r
4665   HDC         hdc;\r
4666   PAINTSTRUCT ps;\r
4667   HFONT       oldFont;\r
4668 \r
4669   if(hdc = BeginPaint(hwnd, &ps)) {\r
4670     if (IsIconic(hwnd)) {\r
4671       DrawIcon(hdc, 2, 2, iconCurrent);\r
4672     } else {\r
4673       if (!appData.monoMode) {\r
4674         SelectPalette(hdc, hPal, FALSE);\r
4675         RealizePalette(hdc);\r
4676       }\r
4677       HDCDrawPosition(hdc, 1, NULL);\r
4678       oldFont =\r
4679         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4680       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4681                  ETO_CLIPPED|ETO_OPAQUE,\r
4682                  &messageRect, messageText, strlen(messageText), NULL);\r
4683       SelectObject(hdc, oldFont);\r
4684       DisplayBothClocks();\r
4685     }\r
4686     EndPaint(hwnd,&ps);\r
4687   }\r
4688 \r
4689   return;\r
4690 }\r
4691 \r
4692 \r
4693 /*\r
4694  * If the user selects on a border boundary, return -1; if off the board,\r
4695  *   return -2.  Otherwise map the event coordinate to the square.\r
4696  * The offset boardRect.left or boardRect.top must already have been\r
4697  *   subtracted from x.\r
4698  */\r
4699 int\r
4700 EventToSquare(int x)\r
4701 {\r
4702   if (x <= 0)\r
4703     return -2;\r
4704   if (x < lineGap)\r
4705     return -1;\r
4706   x -= lineGap;\r
4707   if ((x % (squareSize + lineGap)) >= squareSize)\r
4708     return -1;\r
4709   x /= (squareSize + lineGap);\r
4710   if (x >= BOARD_SIZE)\r
4711     return -2;\r
4712   return x;\r
4713 }\r
4714 \r
4715 typedef struct {\r
4716   char piece;\r
4717   int command;\r
4718   char* name;\r
4719 } DropEnable;\r
4720 \r
4721 DropEnable dropEnables[] = {\r
4722   { 'P', DP_Pawn, "Pawn" },\r
4723   { 'N', DP_Knight, "Knight" },\r
4724   { 'B', DP_Bishop, "Bishop" },\r
4725   { 'R', DP_Rook, "Rook" },\r
4726   { 'Q', DP_Queen, "Queen" },\r
4727 };\r
4728 \r
4729 VOID\r
4730 SetupDropMenu(HMENU hmenu)\r
4731 {\r
4732   int i, count, enable;\r
4733   char *p;\r
4734   extern char white_holding[], black_holding[];\r
4735   char item[MSG_SIZ];\r
4736 \r
4737   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4738     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4739                dropEnables[i].piece);\r
4740     count = 0;\r
4741     while (p && *p++ == dropEnables[i].piece) count++;\r
4742     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4743     enable = count > 0 || !appData.testLegality\r
4744       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4745                       && !appData.icsActive);\r
4746     ModifyMenu(hmenu, dropEnables[i].command,\r
4747                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4748                dropEnables[i].command, item);\r
4749   }\r
4750 }\r
4751 \r
4752 static int fromX = -1, fromY = -1, toX, toY;\r
4753 \r
4754 /* Event handler for mouse messages */\r
4755 VOID\r
4756 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4757 {\r
4758   int x, y;\r
4759   POINT pt;\r
4760   static int recursive = 0;\r
4761   HMENU hmenu;\r
4762   BOOLEAN needsRedraw = FALSE;\r
4763   BOOLEAN saveAnimate;\r
4764   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4765   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4766   ChessMove moveType;\r
4767 \r
4768   if (recursive) {\r
4769     if (message == WM_MBUTTONUP) {\r
4770       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4771          to the middle button: we simulate pressing the left button too!\r
4772          */\r
4773       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4774       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4775     }\r
4776     return;\r
4777   }\r
4778   recursive++;\r
4779   \r
4780   pt.x = LOWORD(lParam);\r
4781   pt.y = HIWORD(lParam);\r
4782   x = EventToSquare(pt.x - boardRect.left);\r
4783   y = EventToSquare(pt.y - boardRect.top);\r
4784   if (!flipView && y >= 0) {\r
4785     y = BOARD_HEIGHT - 1 - y;\r
4786   }\r
4787   if (flipView && x >= 0) {\r
4788     x = BOARD_WIDTH - 1 - x;\r
4789   }\r
4790 \r
4791   switch (message) {\r
4792   case WM_LBUTTONDOWN:\r
4793     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4794         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4795         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4796         if(gameInfo.holdingsWidth && \r
4797                 (WhiteOnMove(currentMove) \r
4798                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4799                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4800             // click in right holdings, for determining promotion piece\r
4801             ChessSquare p = boards[currentMove][y][x];\r
4802             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4803             if(p != EmptySquare) {\r
4804                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4805                 fromX = fromY = -1;\r
4806                 break;\r
4807             }\r
4808         }\r
4809         DrawPosition(FALSE, boards[currentMove]);\r
4810         break;\r
4811     }\r
4812     ErrorPopDown();\r
4813     sameAgain = FALSE;\r
4814     if (y == -2) {\r
4815       /* Downclick vertically off board; check if on clock */\r
4816       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4817         if (gameMode == EditPosition) {\r
4818           SetWhiteToPlayEvent();\r
4819         } else if (gameMode == IcsPlayingBlack ||\r
4820                    gameMode == MachinePlaysWhite) {\r
4821           CallFlagEvent();\r
4822         } else if (gameMode == EditGame) {\r
4823           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4824         }\r
4825       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4826         if (gameMode == EditPosition) {\r
4827           SetBlackToPlayEvent();\r
4828         } else if (gameMode == IcsPlayingWhite ||\r
4829                    gameMode == MachinePlaysBlack) {\r
4830           CallFlagEvent();\r
4831         } else if (gameMode == EditGame) {\r
4832           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4833         }\r
4834       }\r
4835       if (!appData.highlightLastMove) {\r
4836         ClearHighlights();\r
4837         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4838       }\r
4839       fromX = fromY = -1;\r
4840       dragInfo.start.x = dragInfo.start.y = -1;\r
4841       dragInfo.from = dragInfo.start;\r
4842       break;\r
4843     } else if (x < 0 || y < 0\r
4844       /* [HGM] block clicks between board and holdings */\r
4845               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4846               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4847               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4848         /* EditPosition, empty square, or different color piece;\r
4849            click-click move is possible */\r
4850                                ) {\r
4851       break;\r
4852     } else if (fromX == x && fromY == y) {\r
4853       /* Downclick on same square again */\r
4854       ClearHighlights();\r
4855       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4856       sameAgain = TRUE;  \r
4857     } else if (fromX != -1 &&\r
4858                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4859                                                                         ) {\r
4860       /* Downclick on different square. */\r
4861       /* [HGM] if on holdings file, should count as new first click ! */\r
4862       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4863         toX = x;\r
4864         toY = y;\r
4865         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4866            to make sure move is legal before showing promotion popup */\r
4867         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4868         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4869                 fromX = fromY = -1; \r
4870                 ClearHighlights();\r
4871                 DrawPosition(FALSE, boards[currentMove]);\r
4872                 break; \r
4873         } else \r
4874         if(moveType != ImpossibleMove) {\r
4875           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4876           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4877              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4878               appData.alwaysPromoteToQueen) {\r
4879                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4880                   if (!appData.highlightLastMove) {\r
4881                       ClearHighlights();\r
4882                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4883                   }\r
4884           } else\r
4885           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4886                   SetHighlights(fromX, fromY, toX, toY);\r
4887                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4888                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4889                      If promotion to Q is legal, all are legal! */\r
4890                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4891                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4892                     // kludge to temporarily execute move on display, wthout promotng yet\r
4893                     promotionChoice = TRUE;\r
4894                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4895                     boards[currentMove][toY][toX] = p;\r
4896                     DrawPosition(FALSE, boards[currentMove]);\r
4897                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4898                     boards[currentMove][toY][toX] = q;\r
4899                   } else\r
4900                   PromotionPopup(hwnd);\r
4901           } else {       /* not a promotion */\r
4902              if (appData.animate || appData.highlightLastMove) {\r
4903                  SetHighlights(fromX, fromY, toX, toY);\r
4904              } else {\r
4905                  ClearHighlights();\r
4906              }\r
4907              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4908              fromX = fromY = -1;\r
4909              if (appData.animate && !appData.highlightLastMove) {\r
4910                   ClearHighlights();\r
4911                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4912              }\r
4913           }\r
4914           break;\r
4915         }\r
4916         if (gotPremove) {\r
4917             /* [HGM] it seemed that braces were missing here */\r
4918             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4919             fromX = fromY = -1;\r
4920             break;\r
4921         }\r
4922       }\r
4923       ClearHighlights();\r
4924       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4925     }\r
4926     /* First downclick, or restart on a square with same color piece */\r
4927     if (!frozen && OKToStartUserMove(x, y)) {\r
4928       fromX = x;\r
4929       fromY = y;\r
4930       dragInfo.lastpos = pt;\r
4931       dragInfo.from.x = fromX;\r
4932       dragInfo.from.y = fromY;\r
4933       dragInfo.start = dragInfo.from;\r
4934       SetCapture(hwndMain);\r
4935     } else {\r
4936       fromX = fromY = -1;\r
4937       dragInfo.start.x = dragInfo.start.y = -1;\r
4938       dragInfo.from = dragInfo.start;\r
4939       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4940     }\r
4941     break;\r
4942 \r
4943   case WM_LBUTTONUP:\r
4944     ReleaseCapture();\r
4945     if (fromX == -1) break;\r
4946     if (x == fromX && y == fromY) {\r
4947       dragInfo.from.x = dragInfo.from.y = -1;\r
4948       /* Upclick on same square */\r
4949       if (sameAgain) {\r
4950         /* Clicked same square twice: abort click-click move */\r
4951         fromX = fromY = -1;\r
4952         gotPremove = 0;\r
4953         ClearPremoveHighlights();\r
4954       } else {\r
4955         /* First square clicked: start click-click move */\r
4956         SetHighlights(fromX, fromY, -1, -1);\r
4957       }\r
4958       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4959     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4960       /* Errant click; ignore */\r
4961       break;\r
4962     } else {\r
4963       /* Finish drag move. */\r
4964     if (appData.debugMode) {\r
4965         fprintf(debugFP, "release\n");\r
4966     }\r
4967       dragInfo.from.x = dragInfo.from.y = -1;\r
4968       toX = x;\r
4969       toY = y;\r
4970       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4971       appData.animate = appData.animate && !appData.animateDragging;\r
4972       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4973       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4974                 fromX = fromY = -1; \r
4975                 ClearHighlights();\r
4976                 DrawPosition(FALSE, boards[currentMove]);\r
4977                 break; \r
4978       } else \r
4979       if(moveType != ImpossibleMove) {\r
4980           /* [HGM] use move type to determine if move is promotion.\r
4981              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4982           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4983              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4984               appData.alwaysPromoteToQueen) \r
4985                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4986           else \r
4987           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4988                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4989                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4990                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4991                     // kludge to temporarily execute move on display, wthout promotng yet\r
4992                     promotionChoice = TRUE;\r
4993                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4994                     boards[currentMove][toY][toX] = p;\r
4995                     DrawPosition(FALSE, boards[currentMove]);\r
4996                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4997                     boards[currentMove][toY][toX] = q;\r
4998                     break;\r
4999                   } else\r
5000                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5001         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5002       }\r
5003       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5004       appData.animate = saveAnimate;\r
5005       fromX = fromY = -1;\r
5006       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5007         ClearHighlights();\r
5008       }\r
5009       if (appData.animate || appData.animateDragging ||\r
5010           appData.highlightDragging || gotPremove) {\r
5011         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5012       }\r
5013     }\r
5014     dragInfo.start.x = dragInfo.start.y = -1; \r
5015     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5016     break;\r
5017 \r
5018   case WM_MOUSEMOVE:\r
5019     if ((appData.animateDragging || appData.highlightDragging)\r
5020         && (wParam & MK_LBUTTON)\r
5021         && dragInfo.from.x >= 0) \r
5022     {\r
5023       BOOL full_repaint = FALSE;\r
5024 \r
5025       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5026       if (appData.animateDragging) {\r
5027         dragInfo.pos = pt;\r
5028       }\r
5029       if (appData.highlightDragging) {\r
5030         SetHighlights(fromX, fromY, x, y);\r
5031         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5032             full_repaint = TRUE;\r
5033         }\r
5034       }\r
5035       \r
5036       DrawPosition( full_repaint, NULL);\r
5037       \r
5038       dragInfo.lastpos = dragInfo.pos;\r
5039     }\r
5040     break;\r
5041 \r
5042   case WM_MBUTTONDOWN:\r
5043   case WM_RBUTTONDOWN:\r
5044     ErrorPopDown();\r
5045     ReleaseCapture();\r
5046     fromX = fromY = -1;\r
5047     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5048     dragInfo.start.x = dragInfo.start.y = -1;\r
5049     dragInfo.from = dragInfo.start;\r
5050     dragInfo.lastpos = dragInfo.pos;\r
5051     if (appData.highlightDragging) {\r
5052       ClearHighlights();\r
5053     }\r
5054     if(y == -2) {\r
5055       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5056       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5057           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5058       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5059           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5060       }\r
5061     }\r
5062     DrawPosition(TRUE, NULL);\r
5063 \r
5064     switch (gameMode) {\r
5065     case EditPosition:\r
5066     case IcsExamining:\r
5067       if (x < 0 || y < 0) break;\r
5068       fromX = x;\r
5069       fromY = y;\r
5070       if (message == WM_MBUTTONDOWN) {\r
5071         buttonCount = 3;  /* even if system didn't think so */\r
5072         if (wParam & MK_SHIFT) \r
5073           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5074         else\r
5075           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5076       } else { /* message == WM_RBUTTONDOWN */\r
5077 #if 0\r
5078         if (buttonCount == 3) {\r
5079           if (wParam & MK_SHIFT) \r
5080             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5081           else\r
5082             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5083         } else {\r
5084           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5085         }\r
5086 #else\r
5087         /* Just have one menu, on the right button.  Windows users don't\r
5088            think to try the middle one, and sometimes other software steals\r
5089            it, or it doesn't really exist. */\r
5090         if(gameInfo.variant != VariantShogi)\r
5091             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5092         else\r
5093             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5094 #endif\r
5095       }\r
5096       break;\r
5097     case IcsPlayingWhite:\r
5098     case IcsPlayingBlack:\r
5099     case EditGame:\r
5100     case MachinePlaysWhite:\r
5101     case MachinePlaysBlack:\r
5102       if (appData.testLegality &&\r
5103           gameInfo.variant != VariantBughouse &&\r
5104           gameInfo.variant != VariantCrazyhouse) break;\r
5105       if (x < 0 || y < 0) break;\r
5106       fromX = x;\r
5107       fromY = y;\r
5108       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5109       SetupDropMenu(hmenu);\r
5110       MenuPopup(hwnd, pt, hmenu, -1);\r
5111       break;\r
5112     default:\r
5113       break;\r
5114     }\r
5115     break;\r
5116   }\r
5117 \r
5118   recursive--;\r
5119 }\r
5120 \r
5121 /* Preprocess messages for buttons in main window */\r
5122 LRESULT CALLBACK\r
5123 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5124 {\r
5125   int id = GetWindowLong(hwnd, GWL_ID);\r
5126   int i, dir;\r
5127 \r
5128   for (i=0; i<N_BUTTONS; i++) {\r
5129     if (buttonDesc[i].id == id) break;\r
5130   }\r
5131   if (i == N_BUTTONS) return 0;\r
5132   switch (message) {\r
5133   case WM_KEYDOWN:\r
5134     switch (wParam) {\r
5135     case VK_LEFT:\r
5136     case VK_RIGHT:\r
5137       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5138       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5139       return TRUE;\r
5140     }\r
5141     break;\r
5142   case WM_CHAR:\r
5143     switch (wParam) {\r
5144     case '\r':\r
5145       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5146       return TRUE;\r
5147     case '\t':\r
5148       if (appData.icsActive) {\r
5149         if (GetKeyState(VK_SHIFT) < 0) {\r
5150           /* shifted */\r
5151           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5152           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5153           SetFocus(h);\r
5154         } else {\r
5155           /* unshifted */\r
5156           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5157           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5158           SetFocus(h);\r
5159         }\r
5160         return TRUE;\r
5161       }\r
5162       break;\r
5163     default:\r
5164       if (appData.icsActive) {\r
5165         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5166         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5167         SetFocus(h);\r
5168         SendMessage(h, WM_CHAR, wParam, lParam);\r
5169         return TRUE;\r
5170       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5171         PopUpMoveDialog((char)wParam);\r
5172       }\r
5173       break;\r
5174     }\r
5175     break;\r
5176   }\r
5177   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5178 }\r
5179 \r
5180 /* Process messages for Promotion dialog box */\r
5181 LRESULT CALLBACK\r
5182 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5183 {\r
5184   char promoChar;\r
5185 \r
5186   switch (message) {\r
5187   case WM_INITDIALOG: /* message: initialize dialog box */\r
5188     /* Center the dialog over the application window */\r
5189     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5190     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5191       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5192        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5193                SW_SHOW : SW_HIDE);\r
5194     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5195     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5196        (PieceToChar(WhiteAngel) >= 'A' &&\r
5197         PieceToChar(WhiteAngel) != '~' ||\r
5198         PieceToChar(BlackAngel) >= 'A' &&\r
5199         PieceToChar(BlackAngel) != '~'   ) ?\r
5200                SW_SHOW : SW_HIDE);\r
5201     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5202        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5203         PieceToChar(WhiteMarshall) != '~' ||\r
5204         PieceToChar(BlackMarshall) >= 'A' &&\r
5205         PieceToChar(BlackMarshall) != '~'   ) ?\r
5206                SW_SHOW : SW_HIDE);\r
5207     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5208     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5209        gameInfo.variant != VariantShogi ?\r
5210                SW_SHOW : SW_HIDE);\r
5211     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5212        gameInfo.variant != VariantShogi ?\r
5213                SW_SHOW : SW_HIDE);\r
5214     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5215        gameInfo.variant == VariantShogi ?\r
5216                SW_SHOW : SW_HIDE);\r
5217     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5218        gameInfo.variant == VariantShogi ?\r
5219                SW_SHOW : SW_HIDE);\r
5220     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5221        gameInfo.variant == VariantSuper ?\r
5222                SW_SHOW : SW_HIDE);\r
5223     return TRUE;\r
5224 \r
5225   case WM_COMMAND: /* message: received a command */\r
5226     switch (LOWORD(wParam)) {\r
5227     case IDCANCEL:\r
5228       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5229       ClearHighlights();\r
5230       DrawPosition(FALSE, NULL);\r
5231       return TRUE;\r
5232     case PB_King:\r
5233       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5234       break;\r
5235     case PB_Queen:\r
5236       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5237       break;\r
5238     case PB_Rook:\r
5239       promoChar = PieceToChar(BlackRook);\r
5240       break;\r
5241     case PB_Bishop:\r
5242       promoChar = PieceToChar(BlackBishop);\r
5243       break;\r
5244     case PB_Chancellor:\r
5245       promoChar = PieceToChar(BlackMarshall);\r
5246       break;\r
5247     case PB_Archbishop:\r
5248       promoChar = PieceToChar(BlackAngel);\r
5249       break;\r
5250     case PB_Knight:\r
5251       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5252       break;\r
5253     default:\r
5254       return FALSE;\r
5255     }\r
5256     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5257     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5258        only show the popup when we are already sure the move is valid or\r
5259        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5260        will figure out it is a promotion from the promoChar. */\r
5261     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5262     if (!appData.highlightLastMove) {\r
5263       ClearHighlights();\r
5264       DrawPosition(FALSE, NULL);\r
5265     }\r
5266     return TRUE;\r
5267   }\r
5268   return FALSE;\r
5269 }\r
5270 \r
5271 /* Pop up promotion dialog */\r
5272 VOID\r
5273 PromotionPopup(HWND hwnd)\r
5274 {\r
5275   FARPROC lpProc;\r
5276 \r
5277   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5278   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5279     hwnd, (DLGPROC)lpProc);\r
5280   FreeProcInstance(lpProc);\r
5281 }\r
5282 \r
5283 /* Toggle ShowThinking */\r
5284 VOID\r
5285 ToggleShowThinking()\r
5286 {\r
5287   appData.showThinking = !appData.showThinking;\r
5288   ShowThinkingEvent();\r
5289 }\r
5290 \r
5291 VOID\r
5292 LoadGameDialog(HWND hwnd, char* title)\r
5293 {\r
5294   UINT number = 0;\r
5295   FILE *f;\r
5296   char fileTitle[MSG_SIZ];\r
5297   f = OpenFileDialog(hwnd, "rb", "",\r
5298                      appData.oldSaveStyle ? "gam" : "pgn",\r
5299                      GAME_FILT,\r
5300                      title, &number, fileTitle, NULL);\r
5301   if (f != NULL) {\r
5302     cmailMsgLoaded = FALSE;\r
5303     if (number == 0) {\r
5304       int error = GameListBuild(f);\r
5305       if (error) {\r
5306         DisplayError("Cannot build game list", error);\r
5307       } else if (!ListEmpty(&gameList) &&\r
5308                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5309         GameListPopUp(f, fileTitle);\r
5310         return;\r
5311       }\r
5312       GameListDestroy();\r
5313       number = 1;\r
5314     }\r
5315     LoadGame(f, number, fileTitle, FALSE);\r
5316   }\r
5317 }\r
5318 \r
5319 VOID\r
5320 ChangedConsoleFont()\r
5321 {\r
5322   CHARFORMAT cfmt;\r
5323   CHARRANGE tmpsel, sel;\r
5324   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5325   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5326   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5327   PARAFORMAT paraf;\r
5328 \r
5329   cfmt.cbSize = sizeof(CHARFORMAT);\r
5330   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5331   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5332   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5333    * size.  This was undocumented in the version of MSVC++ that I had\r
5334    * when I wrote the code, but is apparently documented now.\r
5335    */\r
5336   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5337   cfmt.bCharSet = f->lf.lfCharSet;\r
5338   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5339   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5340   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5341   /* Why are the following seemingly needed too? */\r
5342   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5343   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5344   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5345   tmpsel.cpMin = 0;\r
5346   tmpsel.cpMax = -1; /*999999?*/\r
5347   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5348   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5349   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5350    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5351    */\r
5352   paraf.cbSize = sizeof(paraf);\r
5353   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5354   paraf.dxStartIndent = 0;\r
5355   paraf.dxOffset = WRAP_INDENT;\r
5356   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5357   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5358 }\r
5359 \r
5360 /*---------------------------------------------------------------------------*\\r
5361  *\r
5362  * Window Proc for main window\r
5363  *\r
5364 \*---------------------------------------------------------------------------*/\r
5365 \r
5366 /* Process messages for main window, etc. */\r
5367 LRESULT CALLBACK\r
5368 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5369 {\r
5370   FARPROC lpProc;\r
5371   int wmId, wmEvent;\r
5372   char *defName;\r
5373   FILE *f;\r
5374   UINT number;\r
5375   char fileTitle[MSG_SIZ];\r
5376   static SnapData sd;\r
5377 \r
5378   switch (message) {\r
5379 \r
5380   case WM_PAINT: /* message: repaint portion of window */\r
5381     PaintProc(hwnd);\r
5382     break;\r
5383 \r
5384   case WM_ERASEBKGND:\r
5385     if (IsIconic(hwnd)) {\r
5386       /* Cheat; change the message */\r
5387       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5388     } else {\r
5389       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5390     }\r
5391     break;\r
5392 \r
5393   case WM_LBUTTONDOWN:\r
5394   case WM_MBUTTONDOWN:\r
5395   case WM_RBUTTONDOWN:\r
5396   case WM_LBUTTONUP:\r
5397   case WM_MBUTTONUP:\r
5398   case WM_RBUTTONUP:\r
5399   case WM_MOUSEMOVE:\r
5400     MouseEvent(hwnd, message, wParam, lParam);\r
5401     break;\r
5402 \r
5403   case WM_CHAR:\r
5404     \r
5405     if (appData.icsActive) {\r
5406       if (wParam == '\t') {\r
5407         if (GetKeyState(VK_SHIFT) < 0) {\r
5408           /* shifted */\r
5409           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5410           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5411           SetFocus(h);\r
5412         } else {\r
5413           /* unshifted */\r
5414           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5415           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5416           SetFocus(h);\r
5417         }\r
5418       } else {\r
5419         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5420         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5421         SetFocus(h);\r
5422         SendMessage(h, message, wParam, lParam);\r
5423       }\r
5424     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5425       PopUpMoveDialog((char)wParam);\r
5426     }\r
5427     break;\r
5428 \r
5429   case WM_PALETTECHANGED:\r
5430     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5431       int nnew;\r
5432       HDC hdc = GetDC(hwndMain);\r
5433       SelectPalette(hdc, hPal, TRUE);\r
5434       nnew = RealizePalette(hdc);\r
5435       if (nnew > 0) {\r
5436         paletteChanged = TRUE;\r
5437 #if 0\r
5438         UpdateColors(hdc);\r
5439 #else\r
5440         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5441 #endif\r
5442       }\r
5443       ReleaseDC(hwnd, hdc);\r
5444     }\r
5445     break;\r
5446 \r
5447   case WM_QUERYNEWPALETTE:\r
5448     if (!appData.monoMode /*&& paletteChanged*/) {\r
5449       int nnew;\r
5450       HDC hdc = GetDC(hwndMain);\r
5451       paletteChanged = FALSE;\r
5452       SelectPalette(hdc, hPal, FALSE);\r
5453       nnew = RealizePalette(hdc);\r
5454       if (nnew > 0) {\r
5455         InvalidateRect(hwnd, &boardRect, FALSE);\r
5456       }\r
5457       ReleaseDC(hwnd, hdc);\r
5458       return TRUE;\r
5459     }\r
5460     return FALSE;\r
5461 \r
5462   case WM_COMMAND: /* message: command from application menu */\r
5463     wmId    = LOWORD(wParam);\r
5464     wmEvent = HIWORD(wParam);\r
5465 \r
5466     switch (wmId) {\r
5467     case IDM_NewGame:\r
5468       ResetGameEvent();\r
5469       AnalysisPopDown();\r
5470       break;\r
5471 \r
5472     case IDM_NewGameFRC:\r
5473       if( NewGameFRC() == 0 ) {\r
5474         ResetGameEvent();\r
5475         AnalysisPopDown();\r
5476       }\r
5477       break;\r
5478 \r
5479     case IDM_NewVariant:\r
5480       NewVariantPopup(hwnd);\r
5481       break;\r
5482 \r
5483     case IDM_LoadGame:\r
5484       LoadGameDialog(hwnd, "Load Game from File");\r
5485       break;\r
5486 \r
5487     case IDM_LoadNextGame:\r
5488       ReloadGame(1);\r
5489       break;\r
5490 \r
5491     case IDM_LoadPrevGame:\r
5492       ReloadGame(-1);\r
5493       break;\r
5494 \r
5495     case IDM_ReloadGame:\r
5496       ReloadGame(0);\r
5497       break;\r
5498 \r
5499     case IDM_LoadPosition:\r
5500       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5501         Reset(FALSE, TRUE);\r
5502       }\r
5503       number = 1;\r
5504       f = OpenFileDialog(hwnd, "rb", "",\r
5505                          appData.oldSaveStyle ? "pos" : "fen",\r
5506                          POSITION_FILT,\r
5507                          "Load Position from File", &number, fileTitle, NULL);\r
5508       if (f != NULL) {\r
5509         LoadPosition(f, number, fileTitle);\r
5510       }\r
5511       break;\r
5512 \r
5513     case IDM_LoadNextPosition:\r
5514       ReloadPosition(1);\r
5515       break;\r
5516 \r
5517     case IDM_LoadPrevPosition:\r
5518       ReloadPosition(-1);\r
5519       break;\r
5520 \r
5521     case IDM_ReloadPosition:\r
5522       ReloadPosition(0);\r
5523       break;\r
5524 \r
5525     case IDM_SaveGame:\r
5526       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5527       f = OpenFileDialog(hwnd, "a", defName,\r
5528                          appData.oldSaveStyle ? "gam" : "pgn",\r
5529                          GAME_FILT,\r
5530                          "Save Game to File", NULL, fileTitle, NULL);\r
5531       if (f != NULL) {\r
5532         SaveGame(f, 0, "");\r
5533       }\r
5534       break;\r
5535 \r
5536     case IDM_SavePosition:\r
5537       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5538       f = OpenFileDialog(hwnd, "a", defName,\r
5539                          appData.oldSaveStyle ? "pos" : "fen",\r
5540                          POSITION_FILT,\r
5541                          "Save Position to File", NULL, fileTitle, NULL);\r
5542       if (f != NULL) {\r
5543         SavePosition(f, 0, "");\r
5544       }\r
5545       break;\r
5546 \r
5547     case IDM_SaveDiagram:\r
5548       defName = "diagram";\r
5549       f = OpenFileDialog(hwnd, "wb", defName,\r
5550                          "bmp",\r
5551                          DIAGRAM_FILT,\r
5552                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5553       if (f != NULL) {\r
5554         SaveDiagram(f);\r
5555       }\r
5556       break;\r
5557 \r
5558     case IDM_CopyGame:\r
5559       CopyGameToClipboard();\r
5560       break;\r
5561 \r
5562     case IDM_PasteGame:\r
5563       PasteGameFromClipboard();\r
5564       break;\r
5565 \r
5566     case IDM_CopyGameListToClipboard:\r
5567       CopyGameListToClipboard();\r
5568       break;\r
5569 \r
5570     /* [AS] Autodetect FEN or PGN data */\r
5571     case IDM_PasteAny:\r
5572       PasteGameOrFENFromClipboard();\r
5573       break;\r
5574 \r
5575     /* [AS] Move history */\r
5576     case IDM_ShowMoveHistory:\r
5577         if( MoveHistoryIsUp() ) {\r
5578             MoveHistoryPopDown();\r
5579         }\r
5580         else {\r
5581             MoveHistoryPopUp();\r
5582         }\r
5583         break;\r
5584 \r
5585     /* [AS] Eval graph */\r
5586     case IDM_ShowEvalGraph:\r
5587         if( EvalGraphIsUp() ) {\r
5588             EvalGraphPopDown();\r
5589         }\r
5590         else {\r
5591             EvalGraphPopUp();\r
5592         }\r
5593         break;\r
5594 \r
5595     /* [AS] Engine output */\r
5596     case IDM_ShowEngineOutput:\r
5597         if( EngineOutputIsUp() ) {\r
5598             EngineOutputPopDown();\r
5599         }\r
5600         else {\r
5601             EngineOutputPopUp();\r
5602         }\r
5603         break;\r
5604 \r
5605     /* [AS] User adjudication */\r
5606     case IDM_UserAdjudication_White:\r
5607         UserAdjudicationEvent( +1 );\r
5608         break;\r
5609 \r
5610     case IDM_UserAdjudication_Black:\r
5611         UserAdjudicationEvent( -1 );\r
5612         break;\r
5613 \r
5614     case IDM_UserAdjudication_Draw:\r
5615         UserAdjudicationEvent( 0 );\r
5616         break;\r
5617 \r
5618     /* [AS] Game list options dialog */\r
5619     case IDM_GameListOptions:\r
5620       GameListOptions();\r
5621       break;\r
5622 \r
5623     case IDM_CopyPosition:\r
5624       CopyFENToClipboard();\r
5625       break;\r
5626 \r
5627     case IDM_PastePosition:\r
5628       PasteFENFromClipboard();\r
5629       break;\r
5630 \r
5631     case IDM_MailMove:\r
5632       MailMoveEvent();\r
5633       break;\r
5634 \r
5635     case IDM_ReloadCMailMsg:\r
5636       Reset(TRUE, TRUE);\r
5637       ReloadCmailMsgEvent(FALSE);\r
5638       break;\r
5639 \r
5640     case IDM_Minimize:\r
5641       ShowWindow(hwnd, SW_MINIMIZE);\r
5642       break;\r
5643 \r
5644     case IDM_Exit:\r
5645       ExitEvent(0);\r
5646       break;\r
5647 \r
5648     case IDM_MachineWhite:\r
5649       MachineWhiteEvent();\r
5650       /*\r
5651        * refresh the tags dialog only if it's visible\r
5652        */\r
5653       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5654           char *tags;\r
5655           tags = PGNTags(&gameInfo);\r
5656           TagsPopUp(tags, CmailMsg());\r
5657           free(tags);\r
5658       }\r
5659       break;\r
5660 \r
5661     case IDM_MachineBlack:\r
5662       MachineBlackEvent();\r
5663       /*\r
5664        * refresh the tags dialog only if it's visible\r
5665        */\r
5666       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5667           char *tags;\r
5668           tags = PGNTags(&gameInfo);\r
5669           TagsPopUp(tags, CmailMsg());\r
5670           free(tags);\r
5671       }\r
5672       break;\r
5673 \r
5674     case IDM_TwoMachines:\r
5675       TwoMachinesEvent();\r
5676       /*\r
5677        * refresh the tags dialog only if it's visible\r
5678        */\r
5679       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5680           char *tags;\r
5681           tags = PGNTags(&gameInfo);\r
5682           TagsPopUp(tags, CmailMsg());\r
5683           free(tags);\r
5684       }\r
5685       break;\r
5686 \r
5687     case IDM_AnalysisMode:\r
5688       if (!first.analysisSupport) {\r
5689         char buf[MSG_SIZ];\r
5690         sprintf(buf, "%s does not support analysis", first.tidy);\r
5691         DisplayError(buf, 0);\r
5692       } else {\r
5693         if (!appData.showThinking) ToggleShowThinking();\r
5694         AnalyzeModeEvent();\r
5695       }\r
5696       break;\r
5697 \r
5698     case IDM_AnalyzeFile:\r
5699       if (!first.analysisSupport) {\r
5700         char buf[MSG_SIZ];\r
5701         sprintf(buf, "%s does not support analysis", first.tidy);\r
5702         DisplayError(buf, 0);\r
5703       } else {\r
5704         if (!appData.showThinking) ToggleShowThinking();\r
5705         AnalyzeFileEvent();\r
5706         LoadGameDialog(hwnd, "Analyze Game from File");\r
5707         AnalysisPeriodicEvent(1);\r
5708       }\r
5709       break;\r
5710 \r
5711     case IDM_IcsClient:\r
5712       IcsClientEvent();\r
5713       break;\r
5714 \r
5715     case IDM_EditGame:\r
5716       EditGameEvent();\r
5717       break;\r
5718 \r
5719     case IDM_EditPosition:\r
5720       EditPositionEvent();\r
5721       break;\r
5722 \r
5723     case IDM_Training:\r
5724       TrainingEvent();\r
5725       break;\r
5726 \r
5727     case IDM_ShowGameList:\r
5728       ShowGameListProc();\r
5729       break;\r
5730 \r
5731     case IDM_EditTags:\r
5732       EditTagsProc();\r
5733       break;\r
5734 \r
5735     case IDM_EditComment:\r
5736       if (commentDialogUp && editComment) {\r
5737         CommentPopDown();\r
5738       } else {\r
5739         EditCommentEvent();\r
5740       }\r
5741       break;\r
5742 \r
5743     case IDM_Pause:\r
5744       PauseEvent();\r
5745       break;\r
5746 \r
5747     case IDM_Accept:\r
5748       AcceptEvent();\r
5749       break;\r
5750 \r
5751     case IDM_Decline:\r
5752       DeclineEvent();\r
5753       break;\r
5754 \r
5755     case IDM_Rematch:\r
5756       RematchEvent();\r
5757       break;\r
5758 \r
5759     case IDM_CallFlag:\r
5760       CallFlagEvent();\r
5761       break;\r
5762 \r
5763     case IDM_Draw:\r
5764       DrawEvent();\r
5765       break;\r
5766 \r
5767     case IDM_Adjourn:\r
5768       AdjournEvent();\r
5769       break;\r
5770 \r
5771     case IDM_Abort:\r
5772       AbortEvent();\r
5773       break;\r
5774 \r
5775     case IDM_Resign:\r
5776       ResignEvent();\r
5777       break;\r
5778 \r
5779     case IDM_StopObserving:\r
5780       StopObservingEvent();\r
5781       break;\r
5782 \r
5783     case IDM_StopExamining:\r
5784       StopExaminingEvent();\r
5785       break;\r
5786 \r
5787     case IDM_TypeInMove:\r
5788       PopUpMoveDialog('\000');\r
5789       break;\r
5790 \r
5791     case IDM_TypeInName:\r
5792       PopUpNameDialog('\000');\r
5793       break;\r
5794 \r
5795     case IDM_Backward:\r
5796       BackwardEvent();\r
5797       SetFocus(hwndMain);\r
5798       break;\r
5799 \r
5800     case IDM_Forward:\r
5801       ForwardEvent();\r
5802       SetFocus(hwndMain);\r
5803       break;\r
5804 \r
5805     case IDM_ToStart:\r
5806       ToStartEvent();\r
5807       SetFocus(hwndMain);\r
5808       break;\r
5809 \r
5810     case IDM_ToEnd:\r
5811       ToEndEvent();\r
5812       SetFocus(hwndMain);\r
5813       break;\r
5814 \r
5815     case IDM_Revert:\r
5816       RevertEvent();\r
5817       break;\r
5818 \r
5819     case IDM_TruncateGame:\r
5820       TruncateGameEvent();\r
5821       break;\r
5822 \r
5823     case IDM_MoveNow:\r
5824       MoveNowEvent();\r
5825       break;\r
5826 \r
5827     case IDM_RetractMove:\r
5828       RetractMoveEvent();\r
5829       break;\r
5830 \r
5831     case IDM_FlipView:\r
5832       flipView = !flipView;\r
5833       DrawPosition(FALSE, NULL);\r
5834       break;\r
5835 \r
5836     case IDM_FlipClock:\r
5837       flipClock = !flipClock;\r
5838       DisplayBothClocks();\r
5839       break;\r
5840 \r
5841     case IDM_GeneralOptions:\r
5842       GeneralOptionsPopup(hwnd);\r
5843       DrawPosition(TRUE, NULL);\r
5844       break;\r
5845 \r
5846     case IDM_BoardOptions:\r
5847       BoardOptionsPopup(hwnd);\r
5848       break;\r
5849 \r
5850     case IDM_EnginePlayOptions:\r
5851       EnginePlayOptionsPopup(hwnd);\r
5852       break;\r
5853 \r
5854     case IDM_OptionsUCI:\r
5855       UciOptionsPopup(hwnd);\r
5856       break;\r
5857 \r
5858     case IDM_IcsOptions:\r
5859       IcsOptionsPopup(hwnd);\r
5860       break;\r
5861 \r
5862     case IDM_Fonts:\r
5863       FontsOptionsPopup(hwnd);\r
5864       break;\r
5865 \r
5866     case IDM_Sounds:\r
5867       SoundOptionsPopup(hwnd);\r
5868       break;\r
5869 \r
5870     case IDM_CommPort:\r
5871       CommPortOptionsPopup(hwnd);\r
5872       break;\r
5873 \r
5874     case IDM_LoadOptions:\r
5875       LoadOptionsPopup(hwnd);\r
5876       break;\r
5877 \r
5878     case IDM_SaveOptions:\r
5879       SaveOptionsPopup(hwnd);\r
5880       break;\r
5881 \r
5882     case IDM_TimeControl:\r
5883       TimeControlOptionsPopup(hwnd);\r
5884       break;\r
5885 \r
5886     case IDM_SaveSettings:\r
5887       SaveSettings(settingsFileName);\r
5888       break;\r
5889 \r
5890     case IDM_SaveSettingsOnExit:\r
5891       saveSettingsOnExit = !saveSettingsOnExit;\r
5892       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5893                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5894                                          MF_CHECKED : MF_UNCHECKED));\r
5895       break;\r
5896 \r
5897     case IDM_Hint:\r
5898       HintEvent();\r
5899       break;\r
5900 \r
5901     case IDM_Book:\r
5902       BookEvent();\r
5903       break;\r
5904 \r
5905     case IDM_AboutGame:\r
5906       AboutGameEvent();\r
5907       break;\r
5908 \r
5909     case IDM_Debug:\r
5910       appData.debugMode = !appData.debugMode;\r
5911       if (appData.debugMode) {\r
5912         char dir[MSG_SIZ];\r
5913         GetCurrentDirectory(MSG_SIZ, dir);\r
5914         SetCurrentDirectory(installDir);\r
5915         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5916         SetCurrentDirectory(dir);\r
5917         setbuf(debugFP, NULL);\r
5918       } else {\r
5919         fclose(debugFP);\r
5920         debugFP = NULL;\r
5921       }\r
5922       break;\r
5923 \r
5924     case IDM_HELPCONTENTS:\r
5925       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5926         MessageBox (GetFocus(),\r
5927                     "Unable to activate help",\r
5928                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5929       }\r
5930       break;\r
5931 \r
5932     case IDM_HELPSEARCH:\r
5933       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5934         MessageBox (GetFocus(),\r
5935                     "Unable to activate help",\r
5936                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5937       }\r
5938       break;\r
5939 \r
5940     case IDM_HELPHELP:\r
5941       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5942         MessageBox (GetFocus(),\r
5943                     "Unable to activate help",\r
5944                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5945       }\r
5946       break;\r
5947 \r
5948     case IDM_ABOUT:\r
5949       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5950       DialogBox(hInst, \r
5951         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5952         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5953       FreeProcInstance(lpProc);\r
5954       break;\r
5955 \r
5956     case IDM_DirectCommand1:\r
5957       AskQuestionEvent("Direct Command",\r
5958                        "Send to chess program:", "", "1");\r
5959       break;\r
5960     case IDM_DirectCommand2:\r
5961       AskQuestionEvent("Direct Command",\r
5962                        "Send to second chess program:", "", "2");\r
5963       break;\r
5964 \r
5965     case EP_WhitePawn:\r
5966       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5967       fromX = fromY = -1;\r
5968       break;\r
5969 \r
5970     case EP_WhiteKnight:\r
5971       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5972       fromX = fromY = -1;\r
5973       break;\r
5974 \r
5975     case EP_WhiteBishop:\r
5976       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5977       fromX = fromY = -1;\r
5978       break;\r
5979 \r
5980     case EP_WhiteRook:\r
5981       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5982       fromX = fromY = -1;\r
5983       break;\r
5984 \r
5985     case EP_WhiteQueen:\r
5986       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5987       fromX = fromY = -1;\r
5988       break;\r
5989 \r
5990     case EP_WhiteFerz:\r
5991       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5992       fromX = fromY = -1;\r
5993       break;\r
5994 \r
5995     case EP_WhiteWazir:\r
5996       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5997       fromX = fromY = -1;\r
5998       break;\r
5999 \r
6000     case EP_WhiteAlfil:\r
6001       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6002       fromX = fromY = -1;\r
6003       break;\r
6004 \r
6005     case EP_WhiteCannon:\r
6006       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6007       fromX = fromY = -1;\r
6008       break;\r
6009 \r
6010     case EP_WhiteCardinal:\r
6011       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6012       fromX = fromY = -1;\r
6013       break;\r
6014 \r
6015     case EP_WhiteMarshall:\r
6016       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6017       fromX = fromY = -1;\r
6018       break;\r
6019 \r
6020     case EP_WhiteKing:\r
6021       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6022       fromX = fromY = -1;\r
6023       break;\r
6024 \r
6025     case EP_BlackPawn:\r
6026       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6027       fromX = fromY = -1;\r
6028       break;\r
6029 \r
6030     case EP_BlackKnight:\r
6031       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6032       fromX = fromY = -1;\r
6033       break;\r
6034 \r
6035     case EP_BlackBishop:\r
6036       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6037       fromX = fromY = -1;\r
6038       break;\r
6039 \r
6040     case EP_BlackRook:\r
6041       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6042       fromX = fromY = -1;\r
6043       break;\r
6044 \r
6045     case EP_BlackQueen:\r
6046       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6047       fromX = fromY = -1;\r
6048       break;\r
6049 \r
6050     case EP_BlackFerz:\r
6051       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6052       fromX = fromY = -1;\r
6053       break;\r
6054 \r
6055     case EP_BlackWazir:\r
6056       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6057       fromX = fromY = -1;\r
6058       break;\r
6059 \r
6060     case EP_BlackAlfil:\r
6061       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6062       fromX = fromY = -1;\r
6063       break;\r
6064 \r
6065     case EP_BlackCannon:\r
6066       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6067       fromX = fromY = -1;\r
6068       break;\r
6069 \r
6070     case EP_BlackCardinal:\r
6071       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6072       fromX = fromY = -1;\r
6073       break;\r
6074 \r
6075     case EP_BlackMarshall:\r
6076       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6077       fromX = fromY = -1;\r
6078       break;\r
6079 \r
6080     case EP_BlackKing:\r
6081       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6082       fromX = fromY = -1;\r
6083       break;\r
6084 \r
6085     case EP_EmptySquare:\r
6086       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6087       fromX = fromY = -1;\r
6088       break;\r
6089 \r
6090     case EP_ClearBoard:\r
6091       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6092       fromX = fromY = -1;\r
6093       break;\r
6094 \r
6095     case EP_White:\r
6096       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6097       fromX = fromY = -1;\r
6098       break;\r
6099 \r
6100     case EP_Black:\r
6101       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6102       fromX = fromY = -1;\r
6103       break;\r
6104 \r
6105     case EP_Promote:\r
6106       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6107       fromX = fromY = -1;\r
6108       break;\r
6109 \r
6110     case EP_Demote:\r
6111       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6112       fromX = fromY = -1;\r
6113       break;\r
6114 \r
6115     case DP_Pawn:\r
6116       DropMenuEvent(WhitePawn, fromX, fromY);\r
6117       fromX = fromY = -1;\r
6118       break;\r
6119 \r
6120     case DP_Knight:\r
6121       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6122       fromX = fromY = -1;\r
6123       break;\r
6124 \r
6125     case DP_Bishop:\r
6126       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6127       fromX = fromY = -1;\r
6128       break;\r
6129 \r
6130     case DP_Rook:\r
6131       DropMenuEvent(WhiteRook, fromX, fromY);\r
6132       fromX = fromY = -1;\r
6133       break;\r
6134 \r
6135     case DP_Queen:\r
6136       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6137       fromX = fromY = -1;\r
6138       break;\r
6139 \r
6140     default:\r
6141       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6142     }\r
6143     break;\r
6144 \r
6145   case WM_TIMER:\r
6146     switch (wParam) {\r
6147     case CLOCK_TIMER_ID:\r
6148       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6149       clockTimerEvent = 0;\r
6150       DecrementClocks(); /* call into back end */\r
6151       break;\r
6152     case LOAD_GAME_TIMER_ID:\r
6153       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6154       loadGameTimerEvent = 0;\r
6155       AutoPlayGameLoop(); /* call into back end */\r
6156       break;\r
6157     case ANALYSIS_TIMER_ID:\r
6158       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
6159           appData.periodicUpdates) {\r
6160         AnalysisPeriodicEvent(0);\r
6161       } else {\r
6162         KillTimer(hwnd, analysisTimerEvent);\r
6163         analysisTimerEvent = 0;\r
6164       }\r
6165       break;\r
6166     case DELAYED_TIMER_ID:\r
6167       KillTimer(hwnd, delayedTimerEvent);\r
6168       delayedTimerEvent = 0;\r
6169       delayedTimerCallback();\r
6170       break;\r
6171     }\r
6172     break;\r
6173 \r
6174   case WM_USER_Input:\r
6175     InputEvent(hwnd, message, wParam, lParam);\r
6176     break;\r
6177 \r
6178   /* [AS] Also move "attached" child windows */\r
6179   case WM_WINDOWPOSCHANGING:\r
6180     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6181         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6182 \r
6183         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6184             /* Window is moving */\r
6185             RECT rcMain;\r
6186 \r
6187             GetWindowRect( hwnd, &rcMain );\r
6188             \r
6189             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6190             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6191             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6192         }\r
6193     }\r
6194     break;\r
6195 \r
6196   /* [AS] Snapping */\r
6197   case WM_ENTERSIZEMOVE:\r
6198     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6199     if (hwnd == hwndMain) {\r
6200       doingSizing = TRUE;\r
6201       lastSizing = 0;\r
6202     }\r
6203     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6204     break;\r
6205 \r
6206   case WM_SIZING:\r
6207     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6208     if (hwnd == hwndMain) {\r
6209       lastSizing = wParam;\r
6210     }\r
6211     break;\r
6212 \r
6213   case WM_MOVING:\r
6214     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6215       return OnMoving( &sd, hwnd, wParam, lParam );\r
6216 \r
6217   case WM_EXITSIZEMOVE:\r
6218     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6219     if (hwnd == hwndMain) {\r
6220       RECT client;\r
6221       doingSizing = FALSE;\r
6222       InvalidateRect(hwnd, &boardRect, FALSE);\r
6223       GetClientRect(hwnd, &client);\r
6224       ResizeBoard(client.right, client.bottom, lastSizing);\r
6225       lastSizing = 0;\r
6226       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6227     }\r
6228     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6229     break;\r
6230 \r
6231   case WM_DESTROY: /* message: window being destroyed */\r
6232     PostQuitMessage(0);\r
6233     break;\r
6234 \r
6235   case WM_CLOSE:\r
6236     if (hwnd == hwndMain) {\r
6237       ExitEvent(0);\r
6238     }\r
6239     break;\r
6240 \r
6241   default:      /* Passes it on if unprocessed */\r
6242     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6243   }\r
6244   return 0;\r
6245 }\r
6246 \r
6247 /*---------------------------------------------------------------------------*\\r
6248  *\r
6249  * Misc utility routines\r
6250  *\r
6251 \*---------------------------------------------------------------------------*/\r
6252 \r
6253 /*\r
6254  * Decent random number generator, at least not as bad as Windows\r
6255  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6256  */\r
6257 unsigned int randstate;\r
6258 \r
6259 int\r
6260 myrandom(void)\r
6261 {\r
6262   randstate = randstate * 1664525 + 1013904223;\r
6263   return (int) randstate & 0x7fffffff;\r
6264 }\r
6265 \r
6266 void\r
6267 mysrandom(unsigned int seed)\r
6268 {\r
6269   randstate = seed;\r
6270 }\r
6271 \r
6272 \r
6273 /* \r
6274  * returns TRUE if user selects a different color, FALSE otherwise \r
6275  */\r
6276 \r
6277 BOOL\r
6278 ChangeColor(HWND hwnd, COLORREF *which)\r
6279 {\r
6280   static BOOL firstTime = TRUE;\r
6281   static DWORD customColors[16];\r
6282   CHOOSECOLOR cc;\r
6283   COLORREF newcolor;\r
6284   int i;\r
6285   ColorClass ccl;\r
6286 \r
6287   if (firstTime) {\r
6288     /* Make initial colors in use available as custom colors */\r
6289     /* Should we put the compiled-in defaults here instead? */\r
6290     i = 0;\r
6291     customColors[i++] = lightSquareColor & 0xffffff;\r
6292     customColors[i++] = darkSquareColor & 0xffffff;\r
6293     customColors[i++] = whitePieceColor & 0xffffff;\r
6294     customColors[i++] = blackPieceColor & 0xffffff;\r
6295     customColors[i++] = highlightSquareColor & 0xffffff;\r
6296     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6297 \r
6298     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6299       customColors[i++] = textAttribs[ccl].color;\r
6300     }\r
6301     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6302     firstTime = FALSE;\r
6303   }\r
6304 \r
6305   cc.lStructSize = sizeof(cc);\r
6306   cc.hwndOwner = hwnd;\r
6307   cc.hInstance = NULL;\r
6308   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6309   cc.lpCustColors = (LPDWORD) customColors;\r
6310   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6311 \r
6312   if (!ChooseColor(&cc)) return FALSE;\r
6313 \r
6314   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6315   if (newcolor == *which) return FALSE;\r
6316   *which = newcolor;\r
6317   return TRUE;\r
6318 \r
6319   /*\r
6320   InitDrawingColors();\r
6321   InvalidateRect(hwnd, &boardRect, FALSE);\r
6322   */\r
6323 }\r
6324 \r
6325 BOOLEAN\r
6326 MyLoadSound(MySound *ms)\r
6327 {\r
6328   BOOL ok = FALSE;\r
6329   struct stat st;\r
6330   FILE *f;\r
6331 \r
6332   if (ms->data) free(ms->data);\r
6333   ms->data = NULL;\r
6334 \r
6335   switch (ms->name[0]) {\r
6336   case NULLCHAR:\r
6337     /* Silence */\r
6338     ok = TRUE;\r
6339     break;\r
6340   case '$':\r
6341     /* System sound from Control Panel.  Don't preload here. */\r
6342     ok = TRUE;\r
6343     break;\r
6344   case '!':\r
6345     if (ms->name[1] == NULLCHAR) {\r
6346       /* "!" alone = silence */\r
6347       ok = TRUE;\r
6348     } else {\r
6349       /* Builtin wave resource.  Error if not found. */\r
6350       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6351       if (h == NULL) break;\r
6352       ms->data = (void *)LoadResource(hInst, h);\r
6353       if (h == NULL) break;\r
6354       ok = TRUE;\r
6355     }\r
6356     break;\r
6357   default:\r
6358     /* .wav file.  Error if not found. */\r
6359     f = fopen(ms->name, "rb");\r
6360     if (f == NULL) break;\r
6361     if (fstat(fileno(f), &st) < 0) break;\r
6362     ms->data = malloc(st.st_size);\r
6363     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6364     fclose(f);\r
6365     ok = TRUE;\r
6366     break;\r
6367   }\r
6368   if (!ok) {\r
6369     char buf[MSG_SIZ];\r
6370     sprintf(buf, "Error loading sound %s", ms->name);\r
6371     DisplayError(buf, GetLastError());\r
6372   }\r
6373   return ok;\r
6374 }\r
6375 \r
6376 BOOLEAN\r
6377 MyPlaySound(MySound *ms)\r
6378 {\r
6379   BOOLEAN ok = FALSE;\r
6380   switch (ms->name[0]) {\r
6381   case NULLCHAR:\r
6382     /* Silence */\r
6383     ok = TRUE;\r
6384     break;\r
6385   case '$':\r
6386     /* System sound from Control Panel (deprecated feature).\r
6387        "$" alone or an unset sound name gets default beep (still in use). */\r
6388     if (ms->name[1]) {\r
6389       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6390     }\r
6391     if (!ok) ok = MessageBeep(MB_OK);\r
6392     break; \r
6393   case '!':\r
6394     /* Builtin wave resource, or "!" alone for silence */\r
6395     if (ms->name[1]) {\r
6396       if (ms->data == NULL) return FALSE;\r
6397       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6398     } else {\r
6399       ok = TRUE;\r
6400     }\r
6401     break;\r
6402   default:\r
6403     /* .wav file.  Error if not found. */\r
6404     if (ms->data == NULL) return FALSE;\r
6405     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6406     break;\r
6407   }\r
6408   /* Don't print an error: this can happen innocently if the sound driver\r
6409      is busy; for instance, if another instance of WinBoard is playing\r
6410      a sound at about the same time. */\r
6411 #if 0\r
6412   if (!ok) {\r
6413     char buf[MSG_SIZ];\r
6414     sprintf(buf, "Error playing sound %s", ms->name);\r
6415     DisplayError(buf, GetLastError());\r
6416   }\r
6417 #endif\r
6418   return ok;\r
6419 }\r
6420 \r
6421 \r
6422 LRESULT CALLBACK\r
6423 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6424 {\r
6425   BOOL ok;\r
6426   OPENFILENAME *ofn;\r
6427   static UINT *number; /* gross that this is static */\r
6428 \r
6429   switch (message) {\r
6430   case WM_INITDIALOG: /* message: initialize dialog box */\r
6431     /* Center the dialog over the application window */\r
6432     ofn = (OPENFILENAME *) lParam;\r
6433     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6434       number = (UINT *) ofn->lCustData;\r
6435       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6436     } else {\r
6437       number = NULL;\r
6438     }\r
6439     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6440     return FALSE;  /* Allow for further processing */\r
6441 \r
6442   case WM_COMMAND:\r
6443     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6444       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6445     }\r
6446     return FALSE;  /* Allow for further processing */\r
6447   }\r
6448   return FALSE;\r
6449 }\r
6450 \r
6451 UINT APIENTRY\r
6452 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6453 {\r
6454   static UINT *number;\r
6455   OPENFILENAME *ofname;\r
6456   OFNOTIFY *ofnot;\r
6457   switch (uiMsg) {\r
6458   case WM_INITDIALOG:\r
6459     ofname = (OPENFILENAME *)lParam;\r
6460     number = (UINT *)(ofname->lCustData);\r
6461     break;\r
6462   case WM_NOTIFY:\r
6463     ofnot = (OFNOTIFY *)lParam;\r
6464     if (ofnot->hdr.code == CDN_FILEOK) {\r
6465       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6466     }\r
6467     break;\r
6468   }\r
6469   return 0;\r
6470 }\r
6471 \r
6472 \r
6473 FILE *\r
6474 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6475                char *nameFilt, char *dlgTitle, UINT *number,\r
6476                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6477 {\r
6478   OPENFILENAME openFileName;\r
6479   char buf1[MSG_SIZ];\r
6480   FILE *f;\r
6481 \r
6482   if (fileName == NULL) fileName = buf1;\r
6483   if (defName == NULL) {\r
6484     strcpy(fileName, "*.");\r
6485     strcat(fileName, defExt);\r
6486   } else {\r
6487     strcpy(fileName, defName);\r
6488   }\r
6489   if (fileTitle) strcpy(fileTitle, "");\r
6490   if (number) *number = 0;\r
6491 \r
6492   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6493   openFileName.hwndOwner         = hwnd;\r
6494   openFileName.hInstance         = (HANDLE) hInst;\r
6495   openFileName.lpstrFilter       = nameFilt;\r
6496   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6497   openFileName.nMaxCustFilter    = 0L;\r
6498   openFileName.nFilterIndex      = 1L;\r
6499   openFileName.lpstrFile         = fileName;\r
6500   openFileName.nMaxFile          = MSG_SIZ;\r
6501   openFileName.lpstrFileTitle    = fileTitle;\r
6502   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6503   openFileName.lpstrInitialDir   = NULL;\r
6504   openFileName.lpstrTitle        = dlgTitle;\r
6505   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6506     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6507     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6508     | (oldDialog ? 0 : OFN_EXPLORER);\r
6509   openFileName.nFileOffset       = 0;\r
6510   openFileName.nFileExtension    = 0;\r
6511   openFileName.lpstrDefExt       = defExt;\r
6512   openFileName.lCustData         = (LONG) number;\r
6513   openFileName.lpfnHook          = oldDialog ?\r
6514     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6515   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6516 \r
6517   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6518                         GetOpenFileName(&openFileName)) {\r
6519     /* open the file */\r
6520     f = fopen(openFileName.lpstrFile, write);\r
6521     if (f == NULL) {\r
6522       MessageBox(hwnd, "File open failed", NULL,\r
6523                  MB_OK|MB_ICONEXCLAMATION);\r
6524       return NULL;\r
6525     }\r
6526   } else {\r
6527     int err = CommDlgExtendedError();\r
6528     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6529     return FALSE;\r
6530   }\r
6531   return f;\r
6532 }\r
6533 \r
6534 \r
6535 \r
6536 VOID APIENTRY\r
6537 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6538 {\r
6539   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6540 \r
6541   /*\r
6542    * Get the first pop-up menu in the menu template. This is the\r
6543    * menu that TrackPopupMenu displays.\r
6544    */\r
6545   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6546 \r
6547   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6548 \r
6549   /*\r
6550    * TrackPopup uses screen coordinates, so convert the\r
6551    * coordinates of the mouse click to screen coordinates.\r
6552    */\r
6553   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6554 \r
6555   /* Draw and track the floating pop-up menu. */\r
6556   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6557                  pt.x, pt.y, 0, hwnd, NULL);\r
6558 \r
6559   /* Destroy the menu.*/\r
6560   DestroyMenu(hmenu);\r
6561 }\r
6562    \r
6563 typedef struct {\r
6564   HWND hDlg, hText;\r
6565   int sizeX, sizeY, newSizeX, newSizeY;\r
6566   HDWP hdwp;\r
6567 } ResizeEditPlusButtonsClosure;\r
6568 \r
6569 BOOL CALLBACK\r
6570 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6571 {\r
6572   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6573   RECT rect;\r
6574   POINT pt;\r
6575 \r
6576   if (hChild == cl->hText) return TRUE;\r
6577   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6578   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6579   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6580   ScreenToClient(cl->hDlg, &pt);\r
6581   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6582     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6583   return TRUE;\r
6584 }\r
6585 \r
6586 /* Resize a dialog that has a (rich) edit field filling most of\r
6587    the top, with a row of buttons below */\r
6588 VOID\r
6589 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6590 {\r
6591   RECT rectText;\r
6592   int newTextHeight, newTextWidth;\r
6593   ResizeEditPlusButtonsClosure cl;\r
6594   \r
6595   /*if (IsIconic(hDlg)) return;*/\r
6596   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6597   \r
6598   cl.hdwp = BeginDeferWindowPos(8);\r
6599 \r
6600   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6601   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6602   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6603   if (newTextHeight < 0) {\r
6604     newSizeY += -newTextHeight;\r
6605     newTextHeight = 0;\r
6606   }\r
6607   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6608     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6609 \r
6610   cl.hDlg = hDlg;\r
6611   cl.hText = hText;\r
6612   cl.sizeX = sizeX;\r
6613   cl.sizeY = sizeY;\r
6614   cl.newSizeX = newSizeX;\r
6615   cl.newSizeY = newSizeY;\r
6616   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6617 \r
6618   EndDeferWindowPos(cl.hdwp);\r
6619 }\r
6620 \r
6621 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6622 {\r
6623     RECT    rChild, rParent;\r
6624     int     wChild, hChild, wParent, hParent;\r
6625     int     wScreen, hScreen, xNew, yNew;\r
6626     HDC     hdc;\r
6627 \r
6628     /* Get the Height and Width of the child window */\r
6629     GetWindowRect (hwndChild, &rChild);\r
6630     wChild = rChild.right - rChild.left;\r
6631     hChild = rChild.bottom - rChild.top;\r
6632 \r
6633     /* Get the Height and Width of the parent window */\r
6634     GetWindowRect (hwndParent, &rParent);\r
6635     wParent = rParent.right - rParent.left;\r
6636     hParent = rParent.bottom - rParent.top;\r
6637 \r
6638     /* Get the display limits */\r
6639     hdc = GetDC (hwndChild);\r
6640     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6641     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6642     ReleaseDC(hwndChild, hdc);\r
6643 \r
6644     /* Calculate new X position, then adjust for screen */\r
6645     xNew = rParent.left + ((wParent - wChild) /2);\r
6646     if (xNew < 0) {\r
6647         xNew = 0;\r
6648     } else if ((xNew+wChild) > wScreen) {\r
6649         xNew = wScreen - wChild;\r
6650     }\r
6651 \r
6652     /* Calculate new Y position, then adjust for screen */\r
6653     if( mode == 0 ) {\r
6654         yNew = rParent.top  + ((hParent - hChild) /2);\r
6655     }\r
6656     else {\r
6657         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6658     }\r
6659 \r
6660     if (yNew < 0) {\r
6661         yNew = 0;\r
6662     } else if ((yNew+hChild) > hScreen) {\r
6663         yNew = hScreen - hChild;\r
6664     }\r
6665 \r
6666     /* Set it, and return */\r
6667     return SetWindowPos (hwndChild, NULL,\r
6668                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6669 }\r
6670 \r
6671 /* Center one window over another */\r
6672 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6673 {\r
6674     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6675 }\r
6676 \r
6677 /*---------------------------------------------------------------------------*\\r
6678  *\r
6679  * Startup Dialog functions\r
6680  *\r
6681 \*---------------------------------------------------------------------------*/\r
6682 void\r
6683 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6684 {\r
6685   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6686 \r
6687   while (*cd != NULL) {\r
6688     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6689     cd++;\r
6690   }\r
6691 }\r
6692 \r
6693 void\r
6694 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6695 {\r
6696   char buf1[ARG_MAX];\r
6697   int len;\r
6698 \r
6699   if (str[0] == '@') {\r
6700     FILE* f = fopen(str + 1, "r");\r
6701     if (f == NULL) {\r
6702       DisplayFatalError(str + 1, errno, 2);\r
6703       return;\r
6704     }\r
6705     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6706     fclose(f);\r
6707     buf1[len] = NULLCHAR;\r
6708     str = buf1;\r
6709   }\r
6710 \r
6711   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6712 \r
6713   for (;;) {\r
6714     char buf[MSG_SIZ];\r
6715     char *end = strchr(str, '\n');\r
6716     if (end == NULL) return;\r
6717     memcpy(buf, str, end - str);\r
6718     buf[end - str] = NULLCHAR;\r
6719     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6720     str = end + 1;\r
6721   }\r
6722 }\r
6723 \r
6724 void\r
6725 SetStartupDialogEnables(HWND hDlg)\r
6726 {\r
6727   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6728     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6729     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6730   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6731     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6732   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6733     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6734   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6735     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6736   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6737     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6738     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6739     IsDlgButtonChecked(hDlg, OPT_View));\r
6740 }\r
6741 \r
6742 char *\r
6743 QuoteForFilename(char *filename)\r
6744 {\r
6745   int dquote, space;\r
6746   dquote = strchr(filename, '"') != NULL;\r
6747   space = strchr(filename, ' ') != NULL;\r
6748   if (dquote || space) {\r
6749     if (dquote) {\r
6750       return "'";\r
6751     } else {\r
6752       return "\"";\r
6753     }\r
6754   } else {\r
6755     return "";\r
6756   }\r
6757 }\r
6758 \r
6759 VOID\r
6760 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6761 {\r
6762   char buf[MSG_SIZ];\r
6763   char *q;\r
6764 \r
6765   InitComboStringsFromOption(hwndCombo, nthnames);\r
6766   q = QuoteForFilename(nthcp);\r
6767   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6768   if (*nthdir != NULLCHAR) {\r
6769     q = QuoteForFilename(nthdir);\r
6770     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6771   }\r
6772   if (*nthcp == NULLCHAR) {\r
6773     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6774   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6775     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6776     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6777   }\r
6778 }\r
6779 \r
6780 LRESULT CALLBACK\r
6781 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6782 {\r
6783   char buf[MSG_SIZ];\r
6784   HANDLE hwndCombo;\r
6785   char *p;\r
6786 \r
6787   switch (message) {\r
6788   case WM_INITDIALOG:\r
6789     /* Center the dialog */\r
6790     CenterWindow (hDlg, GetDesktopWindow());\r
6791     /* Initialize the dialog items */\r
6792     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6793                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6794                   firstChessProgramNames);\r
6795     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6796                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6797                   secondChessProgramNames);\r
6798     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6799     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6800     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6801     if (*appData.icsHelper != NULLCHAR) {\r
6802       char *q = QuoteForFilename(appData.icsHelper);\r
6803       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6804     }\r
6805     if (*appData.icsHost == NULLCHAR) {\r
6806       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6807       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6808     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6809       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6810       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6811     }\r
6812 \r
6813     if (appData.icsActive) {\r
6814       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6815     }\r
6816     else if (appData.noChessProgram) {\r
6817       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6818     }\r
6819     else {\r
6820       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6821     }\r
6822 \r
6823     SetStartupDialogEnables(hDlg);\r
6824     return TRUE;\r
6825 \r
6826   case WM_COMMAND:\r
6827     switch (LOWORD(wParam)) {\r
6828     case IDOK:\r
6829       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6830         strcpy(buf, "/fcp=");\r
6831         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6832         p = buf;\r
6833         ParseArgs(StringGet, &p);\r
6834         strcpy(buf, "/scp=");\r
6835         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6836         p = buf;\r
6837         ParseArgs(StringGet, &p);\r
6838         appData.noChessProgram = FALSE;\r
6839         appData.icsActive = FALSE;\r
6840       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6841         strcpy(buf, "/ics /icshost=");\r
6842         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6843         p = buf;\r
6844         ParseArgs(StringGet, &p);\r
6845         if (appData.zippyPlay) {\r
6846           strcpy(buf, "/fcp=");\r
6847           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6848           p = buf;\r
6849           ParseArgs(StringGet, &p);\r
6850         }\r
6851       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6852         appData.noChessProgram = TRUE;\r
6853         appData.icsActive = FALSE;\r
6854       } else {\r
6855         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6856                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6857         return TRUE;\r
6858       }\r
6859       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6860         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6861         p = buf;\r
6862         ParseArgs(StringGet, &p);\r
6863       }\r
6864       EndDialog(hDlg, TRUE);\r
6865       return TRUE;\r
6866 \r
6867     case IDCANCEL:\r
6868       ExitEvent(0);\r
6869       return TRUE;\r
6870 \r
6871     case IDM_HELPCONTENTS:\r
6872       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6873         MessageBox (GetFocus(),\r
6874                     "Unable to activate help",\r
6875                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6876       }\r
6877       break;\r
6878 \r
6879     default:\r
6880       SetStartupDialogEnables(hDlg);\r
6881       break;\r
6882     }\r
6883     break;\r
6884   }\r
6885   return FALSE;\r
6886 }\r
6887 \r
6888 /*---------------------------------------------------------------------------*\\r
6889  *\r
6890  * About box dialog functions\r
6891  *\r
6892 \*---------------------------------------------------------------------------*/\r
6893 \r
6894 /* Process messages for "About" dialog box */\r
6895 LRESULT CALLBACK\r
6896 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6897 {\r
6898   switch (message) {\r
6899   case WM_INITDIALOG: /* message: initialize dialog box */\r
6900     /* Center the dialog over the application window */\r
6901     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6902     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6903     return (TRUE);\r
6904 \r
6905   case WM_COMMAND: /* message: received a command */\r
6906     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6907         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6908       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6909       return (TRUE);\r
6910     }\r
6911     break;\r
6912   }\r
6913   return (FALSE);\r
6914 }\r
6915 \r
6916 /*---------------------------------------------------------------------------*\\r
6917  *\r
6918  * Comment Dialog functions\r
6919  *\r
6920 \*---------------------------------------------------------------------------*/\r
6921 \r
6922 LRESULT CALLBACK\r
6923 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6924 {\r
6925   static HANDLE hwndText = NULL;\r
6926   int len, newSizeX, newSizeY, flags;\r
6927   static int sizeX, sizeY;\r
6928   char *str;\r
6929   RECT rect;\r
6930   MINMAXINFO *mmi;\r
6931 \r
6932   switch (message) {\r
6933   case WM_INITDIALOG: /* message: initialize dialog box */\r
6934     /* Initialize the dialog items */\r
6935     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6936     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6937     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6938     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6939     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6940     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6941     SetWindowText(hDlg, commentTitle);\r
6942     if (editComment) {\r
6943       SetFocus(hwndText);\r
6944     } else {\r
6945       SetFocus(GetDlgItem(hDlg, IDOK));\r
6946     }\r
6947     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6948                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6949                 MAKELPARAM(FALSE, 0));\r
6950     /* Size and position the dialog */\r
6951     if (!commentDialog) {\r
6952       commentDialog = hDlg;\r
6953       flags = SWP_NOZORDER;\r
6954       GetClientRect(hDlg, &rect);\r
6955       sizeX = rect.right;\r
6956       sizeY = rect.bottom;\r
6957       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6958           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6959         WINDOWPLACEMENT wp;\r
6960         EnsureOnScreen(&commentX, &commentY);\r
6961         wp.length = sizeof(WINDOWPLACEMENT);\r
6962         wp.flags = 0;\r
6963         wp.showCmd = SW_SHOW;\r
6964         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6965         wp.rcNormalPosition.left = commentX;\r
6966         wp.rcNormalPosition.right = commentX + commentW;\r
6967         wp.rcNormalPosition.top = commentY;\r
6968         wp.rcNormalPosition.bottom = commentY + commentH;\r
6969         SetWindowPlacement(hDlg, &wp);\r
6970 \r
6971         GetClientRect(hDlg, &rect);\r
6972         newSizeX = rect.right;\r
6973         newSizeY = rect.bottom;\r
6974         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6975                               newSizeX, newSizeY);\r
6976         sizeX = newSizeX;\r
6977         sizeY = newSizeY;\r
6978       }\r
6979     }\r
6980     return FALSE;\r
6981 \r
6982   case WM_COMMAND: /* message: received a command */\r
6983     switch (LOWORD(wParam)) {\r
6984     case IDOK:\r
6985       if (editComment) {\r
6986         char *p, *q;\r
6987         /* Read changed options from the dialog box */\r
6988         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6989         len = GetWindowTextLength(hwndText);\r
6990         str = (char *) malloc(len + 1);\r
6991         GetWindowText(hwndText, str, len + 1);\r
6992         p = q = str;\r
6993         while (*q) {\r
6994           if (*q == '\r')\r
6995             q++;\r
6996           else\r
6997             *p++ = *q++;\r
6998         }\r
6999         *p = NULLCHAR;\r
7000         ReplaceComment(commentIndex, str);\r
7001         free(str);\r
7002       }\r
7003       CommentPopDown();\r
7004       return TRUE;\r
7005 \r
7006     case IDCANCEL:\r
7007     case OPT_CancelComment:\r
7008       CommentPopDown();\r
7009       return TRUE;\r
7010 \r
7011     case OPT_ClearComment:\r
7012       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7013       break;\r
7014 \r
7015     case OPT_EditComment:\r
7016       EditCommentEvent();\r
7017       return TRUE;\r
7018 \r
7019     default:\r
7020       break;\r
7021     }\r
7022     break;\r
7023 \r
7024   case WM_SIZE:\r
7025     newSizeX = LOWORD(lParam);\r
7026     newSizeY = HIWORD(lParam);\r
7027     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7028     sizeX = newSizeX;\r
7029     sizeY = newSizeY;\r
7030     break;\r
7031 \r
7032   case WM_GETMINMAXINFO:\r
7033     /* Prevent resizing window too small */\r
7034     mmi = (MINMAXINFO *) lParam;\r
7035     mmi->ptMinTrackSize.x = 100;\r
7036     mmi->ptMinTrackSize.y = 100;\r
7037     break;\r
7038   }\r
7039   return FALSE;\r
7040 }\r
7041 \r
7042 VOID\r
7043 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7044 {\r
7045   FARPROC lpProc;\r
7046   char *p, *q;\r
7047 \r
7048   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7049 \r
7050   if (str == NULL) str = "";\r
7051   p = (char *) malloc(2 * strlen(str) + 2);\r
7052   q = p;\r
7053   while (*str) {\r
7054     if (*str == '\n') *q++ = '\r';\r
7055     *q++ = *str++;\r
7056   }\r
7057   *q = NULLCHAR;\r
7058   if (commentText != NULL) free(commentText);\r
7059 \r
7060   commentIndex = index;\r
7061   commentTitle = title;\r
7062   commentText = p;\r
7063   editComment = edit;\r
7064 \r
7065   if (commentDialog) {\r
7066     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7067     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7068   } else {\r
7069     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7070     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7071                  hwndMain, (DLGPROC)lpProc);\r
7072     FreeProcInstance(lpProc);\r
7073   }\r
7074   commentDialogUp = TRUE;\r
7075 }\r
7076 \r
7077 \r
7078 /*---------------------------------------------------------------------------*\\r
7079  *\r
7080  * Type-in move dialog functions\r
7081  * \r
7082 \*---------------------------------------------------------------------------*/\r
7083 \r
7084 LRESULT CALLBACK\r
7085 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7086 {\r
7087   char move[MSG_SIZ];\r
7088   HWND hInput;\r
7089   ChessMove moveType;\r
7090   int fromX, fromY, toX, toY;\r
7091   char promoChar;\r
7092 \r
7093   switch (message) {\r
7094   case WM_INITDIALOG:\r
7095     move[0] = (char) lParam;\r
7096     move[1] = NULLCHAR;\r
7097     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7098     hInput = GetDlgItem(hDlg, OPT_Move);\r
7099     SetWindowText(hInput, move);\r
7100     SetFocus(hInput);\r
7101     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7102     return FALSE;\r
7103 \r
7104   case WM_COMMAND:\r
7105     switch (LOWORD(wParam)) {\r
7106     case IDOK:\r
7107       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7108         gameMode != Training) {\r
7109         DisplayMoveError("Displayed move is not current");\r
7110       } else {\r
7111         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7112         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7113           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7114           if (gameMode != Training)\r
7115               forwardMostMove = currentMove;\r
7116           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7117         } else {\r
7118           DisplayMoveError("Could not parse move");\r
7119         }\r
7120       }\r
7121       EndDialog(hDlg, TRUE);\r
7122       return TRUE;\r
7123     case IDCANCEL:\r
7124       EndDialog(hDlg, FALSE);\r
7125       return TRUE;\r
7126     default:\r
7127       break;\r
7128     }\r
7129     break;\r
7130   }\r
7131   return FALSE;\r
7132 }\r
7133 \r
7134 VOID\r
7135 PopUpMoveDialog(char firstchar)\r
7136 {\r
7137     FARPROC lpProc;\r
7138     \r
7139     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7140         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7141         gameMode == AnalyzeMode || gameMode == EditGame || \r
7142         gameMode == EditPosition || gameMode == IcsExamining ||\r
7143         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7144         gameMode == Training) {\r
7145       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7146       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7147         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7148       FreeProcInstance(lpProc);\r
7149     }\r
7150 }\r
7151 \r
7152 /*---------------------------------------------------------------------------*\\r
7153  *\r
7154  * Type-in name dialog functions\r
7155  * \r
7156 \*---------------------------------------------------------------------------*/\r
7157 \r
7158 LRESULT CALLBACK\r
7159 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7160 {\r
7161   char move[MSG_SIZ];\r
7162   HWND hInput;\r
7163 \r
7164   switch (message) {\r
7165   case WM_INITDIALOG:\r
7166     move[0] = (char) lParam;\r
7167     move[1] = NULLCHAR;\r
7168     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7169     hInput = GetDlgItem(hDlg, OPT_Name);\r
7170     SetWindowText(hInput, move);\r
7171     SetFocus(hInput);\r
7172     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7173     return FALSE;\r
7174 \r
7175   case WM_COMMAND:\r
7176     switch (LOWORD(wParam)) {\r
7177     case IDOK:\r
7178       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7179       appData.userName = strdup(move);\r
7180 \r
7181       EndDialog(hDlg, TRUE);\r
7182       return TRUE;\r
7183     case IDCANCEL:\r
7184       EndDialog(hDlg, FALSE);\r
7185       return TRUE;\r
7186     default:\r
7187       break;\r
7188     }\r
7189     break;\r
7190   }\r
7191   return FALSE;\r
7192 }\r
7193 \r
7194 VOID\r
7195 PopUpNameDialog(char firstchar)\r
7196 {\r
7197     FARPROC lpProc;\r
7198     \r
7199       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7200       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7201         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7202       FreeProcInstance(lpProc);\r
7203 }\r
7204 \r
7205 /*---------------------------------------------------------------------------*\\r
7206  *\r
7207  *  Error dialogs\r
7208  * \r
7209 \*---------------------------------------------------------------------------*/\r
7210 \r
7211 /* Nonmodal error box */\r
7212 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7213                              WPARAM wParam, LPARAM lParam);\r
7214 \r
7215 VOID\r
7216 ErrorPopUp(char *title, char *content)\r
7217 {\r
7218   FARPROC lpProc;\r
7219   char *p, *q;\r
7220   BOOLEAN modal = hwndMain == NULL;\r
7221 \r
7222   p = content;\r
7223   q = errorMessage;\r
7224   while (*p) {\r
7225     if (*p == '\n') {\r
7226       if (modal) {\r
7227         *q++ = ' ';\r
7228         p++;\r
7229       } else {\r
7230         *q++ = '\r';\r
7231         *q++ = *p++;\r
7232       }\r
7233     } else {\r
7234       *q++ = *p++;\r
7235     }\r
7236   }\r
7237   *q = NULLCHAR;\r
7238   strncpy(errorTitle, title, sizeof(errorTitle));\r
7239   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7240   \r
7241   if (modal) {\r
7242     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7243   } else {\r
7244     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7245     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7246                  hwndMain, (DLGPROC)lpProc);\r
7247     FreeProcInstance(lpProc);\r
7248   }\r
7249 }\r
7250 \r
7251 VOID\r
7252 ErrorPopDown()\r
7253 {\r
7254   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7255   if (errorDialog == NULL) return;\r
7256   DestroyWindow(errorDialog);\r
7257   errorDialog = NULL;\r
7258 }\r
7259 \r
7260 LRESULT CALLBACK\r
7261 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7262 {\r
7263   HANDLE hwndText;\r
7264   RECT rChild;\r
7265 \r
7266   switch (message) {\r
7267   case WM_INITDIALOG:\r
7268     GetWindowRect(hDlg, &rChild);\r
7269 \r
7270     /*\r
7271     SetWindowPos(hDlg, NULL, rChild.left,\r
7272       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7273       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7274     */\r
7275 \r
7276     /* \r
7277         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7278         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7279         and it doesn't work when you resize the dialog.\r
7280         For now, just give it a default position.\r
7281     */\r
7282     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7283 \r
7284     errorDialog = hDlg;\r
7285     SetWindowText(hDlg, errorTitle);\r
7286     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7287     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7288     return FALSE;\r
7289 \r
7290   case WM_COMMAND:\r
7291     switch (LOWORD(wParam)) {\r
7292     case IDOK:\r
7293     case IDCANCEL:\r
7294       if (errorDialog == hDlg) errorDialog = NULL;\r
7295       DestroyWindow(hDlg);\r
7296       return TRUE;\r
7297 \r
7298     default:\r
7299       break;\r
7300     }\r
7301     break;\r
7302   }\r
7303   return FALSE;\r
7304 }\r
7305 \r
7306 #ifdef GOTHIC\r
7307 HWND gothicDialog = NULL;\r
7308 \r
7309 LRESULT CALLBACK\r
7310 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7311 {\r
7312   HANDLE hwndText;\r
7313   RECT rChild;\r
7314   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7315 \r
7316   switch (message) {\r
7317   case WM_INITDIALOG:\r
7318     GetWindowRect(hDlg, &rChild);\r
7319 \r
7320     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7321                                                              SWP_NOZORDER);\r
7322 \r
7323     /* \r
7324         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7325         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7326         and it doesn't work when you resize the dialog.\r
7327         For now, just give it a default position.\r
7328     */\r
7329     gothicDialog = hDlg;\r
7330     SetWindowText(hDlg, errorTitle);\r
7331     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7332     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7333     return FALSE;\r
7334 \r
7335   case WM_COMMAND:\r
7336     switch (LOWORD(wParam)) {\r
7337     case IDOK:\r
7338     case IDCANCEL:\r
7339       if (errorDialog == hDlg) errorDialog = NULL;\r
7340       DestroyWindow(hDlg);\r
7341       return TRUE;\r
7342 \r
7343     default:\r
7344       break;\r
7345     }\r
7346     break;\r
7347   }\r
7348   return FALSE;\r
7349 }\r
7350 \r
7351 VOID\r
7352 GothicPopUp(char *title, VariantClass variant)\r
7353 {\r
7354   FARPROC lpProc;\r
7355   char *p, *q;\r
7356   BOOLEAN modal = hwndMain == NULL;\r
7357   static char *lastTitle;\r
7358 \r
7359   strncpy(errorTitle, title, sizeof(errorTitle));\r
7360   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7361 \r
7362   if(lastTitle != title && gothicDialog != NULL) {\r
7363     DestroyWindow(gothicDialog);\r
7364     gothicDialog = NULL;\r
7365   }\r
7366   if(variant != VariantNormal && gothicDialog == NULL) {\r
7367     title = lastTitle;\r
7368     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7369     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7370                  hwndMain, (DLGPROC)lpProc);\r
7371     FreeProcInstance(lpProc);\r
7372   }\r
7373 }\r
7374 #endif\r
7375 \r
7376 /*---------------------------------------------------------------------------*\\r
7377  *\r
7378  *  Ics Interaction console functions\r
7379  *\r
7380 \*---------------------------------------------------------------------------*/\r
7381 \r
7382 #define HISTORY_SIZE 64\r
7383 static char *history[HISTORY_SIZE];\r
7384 int histIn = 0, histP = 0;\r
7385 \r
7386 VOID\r
7387 SaveInHistory(char *cmd)\r
7388 {\r
7389   if (history[histIn] != NULL) {\r
7390     free(history[histIn]);\r
7391     history[histIn] = NULL;\r
7392   }\r
7393   if (*cmd == NULLCHAR) return;\r
7394   history[histIn] = StrSave(cmd);\r
7395   histIn = (histIn + 1) % HISTORY_SIZE;\r
7396   if (history[histIn] != NULL) {\r
7397     free(history[histIn]);\r
7398     history[histIn] = NULL;\r
7399   }\r
7400   histP = histIn;\r
7401 }\r
7402 \r
7403 char *\r
7404 PrevInHistory(char *cmd)\r
7405 {\r
7406   int newhp;\r
7407   if (histP == histIn) {\r
7408     if (history[histIn] != NULL) free(history[histIn]);\r
7409     history[histIn] = StrSave(cmd);\r
7410   }\r
7411   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7412   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7413   histP = newhp;\r
7414   return history[histP];\r
7415 }\r
7416 \r
7417 char *\r
7418 NextInHistory()\r
7419 {\r
7420   if (histP == histIn) return NULL;\r
7421   histP = (histP + 1) % HISTORY_SIZE;\r
7422   return history[histP];\r
7423 }\r
7424 \r
7425 typedef struct {\r
7426   char *item;\r
7427   char *command;\r
7428   BOOLEAN getname;\r
7429   BOOLEAN immediate;\r
7430 } IcsTextMenuEntry;\r
7431 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7432 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7433 \r
7434 void\r
7435 ParseIcsTextMenu(char *icsTextMenuString)\r
7436 {\r
7437   int flags = 0;\r
7438   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7439   char *p = icsTextMenuString;\r
7440   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7441     free(e->item);\r
7442     e->item = NULL;\r
7443     if (e->command != NULL) {\r
7444       free(e->command);\r
7445       e->command = NULL;\r
7446     }\r
7447     e++;\r
7448   }\r
7449   e = icsTextMenuEntry;\r
7450   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7451     if (*p == ';' || *p == '\n') {\r
7452       e->item = strdup("-");\r
7453       e->command = NULL;\r
7454       p++;\r
7455     } else if (*p == '-') {\r
7456       e->item = strdup("-");\r
7457       e->command = NULL;\r
7458       p++;\r
7459       if (*p) p++;\r
7460     } else {\r
7461       char *q, *r, *s, *t;\r
7462       char c;\r
7463       q = strchr(p, ',');\r
7464       if (q == NULL) break;\r
7465       *q = NULLCHAR;\r
7466       r = strchr(q + 1, ',');\r
7467       if (r == NULL) break;\r
7468       *r = NULLCHAR;\r
7469       s = strchr(r + 1, ',');\r
7470       if (s == NULL) break;\r
7471       *s = NULLCHAR;\r
7472       c = ';';\r
7473       t = strchr(s + 1, c);\r
7474       if (t == NULL) {\r
7475         c = '\n';\r
7476         t = strchr(s + 1, c);\r
7477       }\r
7478       if (t != NULL) *t = NULLCHAR;\r
7479       e->item = strdup(p);\r
7480       e->command = strdup(q + 1);\r
7481       e->getname = *(r + 1) != '0';\r
7482       e->immediate = *(s + 1) != '0';\r
7483       *q = ',';\r
7484       *r = ',';\r
7485       *s = ',';\r
7486       if (t == NULL) break;\r
7487       *t = c;\r
7488       p = t + 1;\r
7489     }\r
7490     e++;\r
7491   } \r
7492 }\r
7493 \r
7494 HMENU\r
7495 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7496 {\r
7497   HMENU hmenu, h;\r
7498   int i = 0;\r
7499   hmenu = LoadMenu(hInst, "TextMenu");\r
7500   h = GetSubMenu(hmenu, 0);\r
7501   while (e->item) {\r
7502     if (strcmp(e->item, "-") == 0) {\r
7503       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7504     } else {\r
7505       if (e->item[0] == '|') {\r
7506         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7507                    IDM_CommandX + i, &e->item[1]);\r
7508       } else {\r
7509         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7510       }\r
7511     }\r
7512     e++;\r
7513     i++;\r
7514   } \r
7515   return hmenu;\r
7516 }\r
7517 \r
7518 WNDPROC consoleTextWindowProc;\r
7519 \r
7520 void\r
7521 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7522 {\r
7523   char buf[MSG_SIZ], name[MSG_SIZ];\r
7524   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7525   CHARRANGE sel;\r
7526 \r
7527   if (!getname) {\r
7528     SetWindowText(hInput, command);\r
7529     if (immediate) {\r
7530       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7531     } else {\r
7532       sel.cpMin = 999999;\r
7533       sel.cpMax = 999999;\r
7534       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7535       SetFocus(hInput);\r
7536     }\r
7537     return;\r
7538   }    \r
7539   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7540   if (sel.cpMin == sel.cpMax) {\r
7541     /* Expand to surrounding word */\r
7542     TEXTRANGE tr;\r
7543     do {\r
7544       tr.chrg.cpMax = sel.cpMin;\r
7545       tr.chrg.cpMin = --sel.cpMin;\r
7546       if (sel.cpMin < 0) break;\r
7547       tr.lpstrText = name;\r
7548       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7549     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7550     sel.cpMin++;\r
7551 \r
7552     do {\r
7553       tr.chrg.cpMin = sel.cpMax;\r
7554       tr.chrg.cpMax = ++sel.cpMax;\r
7555       tr.lpstrText = name;\r
7556       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7557     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7558     sel.cpMax--;\r
7559 \r
7560     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7561       MessageBeep(MB_ICONEXCLAMATION);\r
7562       return;\r
7563     }\r
7564     tr.chrg = sel;\r
7565     tr.lpstrText = name;\r
7566     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7567   } else {\r
7568     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7569       MessageBeep(MB_ICONEXCLAMATION);\r
7570       return;\r
7571     }\r
7572     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7573   }\r
7574   if (immediate) {\r
7575     sprintf(buf, "%s %s", command, name);\r
7576     SetWindowText(hInput, buf);\r
7577     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7578   } else {\r
7579     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7580     SetWindowText(hInput, buf);\r
7581     sel.cpMin = 999999;\r
7582     sel.cpMax = 999999;\r
7583     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7584     SetFocus(hInput);\r
7585   }\r
7586 }\r
7587 \r
7588 LRESULT CALLBACK \r
7589 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7590 {\r
7591   HWND hInput;\r
7592   CHARRANGE sel;\r
7593 \r
7594   switch (message) {\r
7595   case WM_KEYDOWN:\r
7596     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7597     switch (wParam) {\r
7598     case VK_PRIOR:\r
7599       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7600       return 0;\r
7601     case VK_NEXT:\r
7602       sel.cpMin = 999999;\r
7603       sel.cpMax = 999999;\r
7604       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7605       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7606       return 0;\r
7607     }\r
7608     break;\r
7609   case WM_CHAR:\r
7610     if (wParam == '\t') {\r
7611       if (GetKeyState(VK_SHIFT) < 0) {\r
7612         /* shifted */\r
7613         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7614         if (buttonDesc[0].hwnd) {\r
7615           SetFocus(buttonDesc[0].hwnd);\r
7616         } else {\r
7617           SetFocus(hwndMain);\r
7618         }\r
7619       } else {\r
7620         /* unshifted */\r
7621         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7622       }\r
7623     } else {\r
7624       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7625       SetFocus(hInput);\r
7626       SendMessage(hInput, message, wParam, lParam);\r
7627     }\r
7628     return 0;\r
7629   case WM_PASTE:\r
7630     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7631     SetFocus(hInput);\r
7632     return SendMessage(hInput, message, wParam, lParam);\r
7633   case WM_MBUTTONDOWN:\r
7634     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7635   case WM_RBUTTONDOWN:\r
7636     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7637       /* Move selection here if it was empty */\r
7638       POINT pt;\r
7639       pt.x = LOWORD(lParam);\r
7640       pt.y = HIWORD(lParam);\r
7641       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7642       if (sel.cpMin == sel.cpMax) {\r
7643         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7644         sel.cpMax = sel.cpMin;\r
7645         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7646       }\r
7647       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7648     }\r
7649     return 0;\r
7650   case WM_RBUTTONUP:\r
7651     if (GetKeyState(VK_SHIFT) & ~1) {\r
7652       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7653         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7654     } else {\r
7655       POINT pt;\r
7656       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7657       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7658       if (sel.cpMin == sel.cpMax) {\r
7659         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7660         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7661       }\r
7662       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7663         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7664       }\r
7665       pt.x = LOWORD(lParam);\r
7666       pt.y = HIWORD(lParam);\r
7667       MenuPopup(hwnd, pt, hmenu, -1);\r
7668     }\r
7669     return 0;\r
7670   case WM_COMMAND:\r
7671     switch (LOWORD(wParam)) {\r
7672     case IDM_QuickPaste:\r
7673       {\r
7674         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7675         if (sel.cpMin == sel.cpMax) {\r
7676           MessageBeep(MB_ICONEXCLAMATION);\r
7677           return 0;\r
7678         }\r
7679         SendMessage(hwnd, WM_COPY, 0, 0);\r
7680         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7681         SendMessage(hInput, WM_PASTE, 0, 0);\r
7682         SetFocus(hInput);\r
7683         return 0;\r
7684       }\r
7685     case IDM_Cut:\r
7686       SendMessage(hwnd, WM_CUT, 0, 0);\r
7687       return 0;\r
7688     case IDM_Paste:\r
7689       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7690       return 0;\r
7691     case IDM_Copy:\r
7692       SendMessage(hwnd, WM_COPY, 0, 0);\r
7693       return 0;\r
7694     default:\r
7695       {\r
7696         int i = LOWORD(wParam) - IDM_CommandX;\r
7697         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7698             icsTextMenuEntry[i].command != NULL) {\r
7699           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7700                    icsTextMenuEntry[i].getname,\r
7701                    icsTextMenuEntry[i].immediate);\r
7702           return 0;\r
7703         }\r
7704       }\r
7705       break;\r
7706     }\r
7707     break;\r
7708   }\r
7709   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7710 }\r
7711 \r
7712 WNDPROC consoleInputWindowProc;\r
7713 \r
7714 LRESULT CALLBACK\r
7715 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7716 {\r
7717   char buf[MSG_SIZ];\r
7718   char *p;\r
7719   static BOOL sendNextChar = FALSE;\r
7720   static BOOL quoteNextChar = FALSE;\r
7721   InputSource *is = consoleInputSource;\r
7722   CHARFORMAT cf;\r
7723   CHARRANGE sel;\r
7724 \r
7725   switch (message) {\r
7726   case WM_CHAR:\r
7727     if (!appData.localLineEditing || sendNextChar) {\r
7728       is->buf[0] = (CHAR) wParam;\r
7729       is->count = 1;\r
7730       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7731       sendNextChar = FALSE;\r
7732       return 0;\r
7733     }\r
7734     if (quoteNextChar) {\r
7735       buf[0] = (char) wParam;\r
7736       buf[1] = NULLCHAR;\r
7737       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7738       quoteNextChar = FALSE;\r
7739       return 0;\r
7740     }\r
7741     switch (wParam) {\r
7742     case '\r':   /* Enter key */\r
7743       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7744       if (consoleEcho) SaveInHistory(is->buf);\r
7745       is->buf[is->count++] = '\n';\r
7746       is->buf[is->count] = NULLCHAR;\r
7747       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7748       if (consoleEcho) {\r
7749         ConsoleOutput(is->buf, is->count, TRUE);\r
7750       } else if (appData.localLineEditing) {\r
7751         ConsoleOutput("\n", 1, TRUE);\r
7752       }\r
7753       /* fall thru */\r
7754     case '\033': /* Escape key */\r
7755       SetWindowText(hwnd, "");\r
7756       cf.cbSize = sizeof(CHARFORMAT);\r
7757       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7758       if (consoleEcho) {\r
7759         cf.crTextColor = textAttribs[ColorNormal].color;\r
7760       } else {\r
7761         cf.crTextColor = COLOR_ECHOOFF;\r
7762       }\r
7763       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7764       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7765       return 0;\r
7766     case '\t':   /* Tab key */\r
7767       if (GetKeyState(VK_SHIFT) < 0) {\r
7768         /* shifted */\r
7769         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7770       } else {\r
7771         /* unshifted */\r
7772         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7773         if (buttonDesc[0].hwnd) {\r
7774           SetFocus(buttonDesc[0].hwnd);\r
7775         } else {\r
7776           SetFocus(hwndMain);\r
7777         }\r
7778       }\r
7779       return 0;\r
7780     case '\023': /* Ctrl+S */\r
7781       sendNextChar = TRUE;\r
7782       return 0;\r
7783     case '\021': /* Ctrl+Q */\r
7784       quoteNextChar = TRUE;\r
7785       return 0;\r
7786     default:\r
7787       break;\r
7788     }\r
7789     break;\r
7790   case WM_KEYDOWN:\r
7791     switch (wParam) {\r
7792     case VK_UP:\r
7793       GetWindowText(hwnd, buf, MSG_SIZ);\r
7794       p = PrevInHistory(buf);\r
7795       if (p != NULL) {\r
7796         SetWindowText(hwnd, p);\r
7797         sel.cpMin = 999999;\r
7798         sel.cpMax = 999999;\r
7799         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7800         return 0;\r
7801       }\r
7802       break;\r
7803     case VK_DOWN:\r
7804       p = NextInHistory();\r
7805       if (p != NULL) {\r
7806         SetWindowText(hwnd, p);\r
7807         sel.cpMin = 999999;\r
7808         sel.cpMax = 999999;\r
7809         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7810         return 0;\r
7811       }\r
7812       break;\r
7813     case VK_HOME:\r
7814     case VK_END:\r
7815       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7816       /* fall thru */\r
7817     case VK_PRIOR:\r
7818     case VK_NEXT:\r
7819       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7820       return 0;\r
7821     }\r
7822     break;\r
7823   case WM_MBUTTONDOWN:\r
7824     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7825       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7826     break;\r
7827   case WM_RBUTTONUP:\r
7828     if (GetKeyState(VK_SHIFT) & ~1) {\r
7829       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7830         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7831     } else {\r
7832       POINT pt;\r
7833       HMENU hmenu;\r
7834       hmenu = LoadMenu(hInst, "InputMenu");\r
7835       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7836       if (sel.cpMin == sel.cpMax) {\r
7837         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7838         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7839       }\r
7840       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7841         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7842       }\r
7843       pt.x = LOWORD(lParam);\r
7844       pt.y = HIWORD(lParam);\r
7845       MenuPopup(hwnd, pt, hmenu, -1);\r
7846     }\r
7847     return 0;\r
7848   case WM_COMMAND:\r
7849     switch (LOWORD(wParam)) { \r
7850     case IDM_Undo:\r
7851       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7852       return 0;\r
7853     case IDM_SelectAll:\r
7854       sel.cpMin = 0;\r
7855       sel.cpMax = -1; /*999999?*/\r
7856       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7857       return 0;\r
7858     case IDM_Cut:\r
7859       SendMessage(hwnd, WM_CUT, 0, 0);\r
7860       return 0;\r
7861     case IDM_Paste:\r
7862       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7863       return 0;\r
7864     case IDM_Copy:\r
7865       SendMessage(hwnd, WM_COPY, 0, 0);\r
7866       return 0;\r
7867     }\r
7868     break;\r
7869   }\r
7870   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7871 }\r
7872 \r
7873 #define CO_MAX  100000\r
7874 #define CO_TRIM   1000\r
7875 \r
7876 LRESULT CALLBACK\r
7877 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7878 {\r
7879   static SnapData sd;\r
7880   static HWND hText, hInput, hFocus;\r
7881   InputSource *is = consoleInputSource;\r
7882   RECT rect;\r
7883   static int sizeX, sizeY;\r
7884   int newSizeX, newSizeY;\r
7885   MINMAXINFO *mmi;\r
7886 \r
7887   switch (message) {\r
7888   case WM_INITDIALOG: /* message: initialize dialog box */\r
7889     hwndConsole = hDlg;\r
7890     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7891     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7892     SetFocus(hInput);\r
7893     consoleTextWindowProc = (WNDPROC)\r
7894       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7895     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7896     consoleInputWindowProc = (WNDPROC)\r
7897       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7898     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7899     Colorize(ColorNormal, TRUE);\r
7900     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7901     ChangedConsoleFont();\r
7902     GetClientRect(hDlg, &rect);\r
7903     sizeX = rect.right;\r
7904     sizeY = rect.bottom;\r
7905     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7906         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7907       WINDOWPLACEMENT wp;\r
7908       EnsureOnScreen(&consoleX, &consoleY);\r
7909       wp.length = sizeof(WINDOWPLACEMENT);\r
7910       wp.flags = 0;\r
7911       wp.showCmd = SW_SHOW;\r
7912       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7913       wp.rcNormalPosition.left = consoleX;\r
7914       wp.rcNormalPosition.right = consoleX + consoleW;\r
7915       wp.rcNormalPosition.top = consoleY;\r
7916       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7917       SetWindowPlacement(hDlg, &wp);\r
7918     }\r
7919     return FALSE;\r
7920 \r
7921   case WM_SETFOCUS:\r
7922     SetFocus(hInput);\r
7923     return 0;\r
7924 \r
7925   case WM_CLOSE:\r
7926     ExitEvent(0);\r
7927     /* not reached */\r
7928     break;\r
7929 \r
7930   case WM_SIZE:\r
7931     if (IsIconic(hDlg)) break;\r
7932     newSizeX = LOWORD(lParam);\r
7933     newSizeY = HIWORD(lParam);\r
7934     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7935       RECT rectText, rectInput;\r
7936       POINT pt;\r
7937       int newTextHeight, newTextWidth;\r
7938       GetWindowRect(hText, &rectText);\r
7939       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7940       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7941       if (newTextHeight < 0) {\r
7942         newSizeY += -newTextHeight;\r
7943         newTextHeight = 0;\r
7944       }\r
7945       SetWindowPos(hText, NULL, 0, 0,\r
7946         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7947       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7948       pt.x = rectInput.left;\r
7949       pt.y = rectInput.top + newSizeY - sizeY;\r
7950       ScreenToClient(hDlg, &pt);\r
7951       SetWindowPos(hInput, NULL, \r
7952         pt.x, pt.y, /* needs client coords */   \r
7953         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7954         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7955     }\r
7956     sizeX = newSizeX;\r
7957     sizeY = newSizeY;\r
7958     break;\r
7959 \r
7960   case WM_GETMINMAXINFO:\r
7961     /* Prevent resizing window too small */\r
7962     mmi = (MINMAXINFO *) lParam;\r
7963     mmi->ptMinTrackSize.x = 100;\r
7964     mmi->ptMinTrackSize.y = 100;\r
7965     break;\r
7966 \r
7967   /* [AS] Snapping */\r
7968   case WM_ENTERSIZEMOVE:\r
7969     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7970 \r
7971   case WM_SIZING:\r
7972     return OnSizing( &sd, hDlg, wParam, lParam );\r
7973 \r
7974   case WM_MOVING:\r
7975     return OnMoving( &sd, hDlg, wParam, lParam );\r
7976 \r
7977   case WM_EXITSIZEMOVE:\r
7978     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7979   }\r
7980 \r
7981   return DefWindowProc(hDlg, message, wParam, lParam);\r
7982 }\r
7983 \r
7984 \r
7985 VOID\r
7986 ConsoleCreate()\r
7987 {\r
7988   HWND hCons;\r
7989   if (hwndConsole) return;\r
7990   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7991   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7992 }\r
7993 \r
7994 \r
7995 VOID\r
7996 ConsoleOutput(char* data, int length, int forceVisible)\r
7997 {\r
7998   HWND hText;\r
7999   int trim, exlen;\r
8000   char *p, *q;\r
8001   char buf[CO_MAX+1];\r
8002   POINT pEnd;\r
8003   RECT rect;\r
8004   static int delayLF = 0;\r
8005   CHARRANGE savesel, sel;\r
8006 \r
8007   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8008   p = data;\r
8009   q = buf;\r
8010   if (delayLF) {\r
8011     *q++ = '\r';\r
8012     *q++ = '\n';\r
8013     delayLF = 0;\r
8014   }\r
8015   while (length--) {\r
8016     if (*p == '\n') {\r
8017       if (*++p) {\r
8018         *q++ = '\r';\r
8019         *q++ = '\n';\r
8020       } else {\r
8021         delayLF = 1;\r
8022       }\r
8023     } else if (*p == '\007') {\r
8024        MyPlaySound(&sounds[(int)SoundBell]);\r
8025        p++;\r
8026     } else {\r
8027       *q++ = *p++;\r
8028     }\r
8029   }\r
8030   *q = NULLCHAR;\r
8031   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8032   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8033   /* Save current selection */\r
8034   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8035   exlen = GetWindowTextLength(hText);\r
8036   /* Find out whether current end of text is visible */\r
8037   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8038   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8039   /* Trim existing text if it's too long */\r
8040   if (exlen + (q - buf) > CO_MAX) {\r
8041     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8042     sel.cpMin = 0;\r
8043     sel.cpMax = trim;\r
8044     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8045     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8046     exlen -= trim;\r
8047     savesel.cpMin -= trim;\r
8048     savesel.cpMax -= trim;\r
8049     if (exlen < 0) exlen = 0;\r
8050     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8051     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8052   }\r
8053   /* Append the new text */\r
8054   sel.cpMin = exlen;\r
8055   sel.cpMax = exlen;\r
8056   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8057   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8058   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8059   if (forceVisible || exlen == 0 ||\r
8060       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8061        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8062     /* Scroll to make new end of text visible if old end of text\r
8063        was visible or new text is an echo of user typein */\r
8064     sel.cpMin = 9999999;\r
8065     sel.cpMax = 9999999;\r
8066     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8067     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8068     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8069     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8070   }\r
8071   if (savesel.cpMax == exlen || forceVisible) {\r
8072     /* Move insert point to new end of text if it was at the old\r
8073        end of text or if the new text is an echo of user typein */\r
8074     sel.cpMin = 9999999;\r
8075     sel.cpMax = 9999999;\r
8076     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8077   } else {\r
8078     /* Restore previous selection */\r
8079     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8080   }\r
8081   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8082 }\r
8083 \r
8084 /*---------*/\r
8085 \r
8086 \r
8087 void\r
8088 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8089 {\r
8090   char buf[100];\r
8091   char *str;\r
8092   COLORREF oldFg, oldBg;\r
8093   HFONT oldFont;\r
8094   RECT rect;\r
8095 \r
8096   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8097 \r
8098   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8099   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8100   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8101 \r
8102   rect.left = x;\r
8103   rect.right = x + squareSize;\r
8104   rect.top  = y;\r
8105   rect.bottom = y + squareSize;\r
8106   str = buf;\r
8107 \r
8108   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8109                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8110              y, ETO_CLIPPED|ETO_OPAQUE,\r
8111              &rect, str, strlen(str), NULL);\r
8112 \r
8113   (void) SetTextColor(hdc, oldFg);\r
8114   (void) SetBkColor(hdc, oldBg);\r
8115   (void) SelectObject(hdc, oldFont);\r
8116 }\r
8117 \r
8118 void\r
8119 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8120               RECT *rect, char *color, char *flagFell)\r
8121 {\r
8122   char buf[100];\r
8123   char *str;\r
8124   COLORREF oldFg, oldBg;\r
8125   HFONT oldFont;\r
8126 \r
8127   if (appData.clockMode) {\r
8128     if (tinyLayout)\r
8129       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8130     else\r
8131       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8132     str = buf;\r
8133   } else {\r
8134     str = color;\r
8135   }\r
8136 \r
8137   if (highlight) {\r
8138     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8139     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8140   } else {\r
8141     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8142     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8143   }\r
8144   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8145 \r
8146   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8147              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8148              rect, str, strlen(str), NULL);\r
8149 \r
8150   (void) SetTextColor(hdc, oldFg);\r
8151   (void) SetBkColor(hdc, oldBg);\r
8152   (void) SelectObject(hdc, oldFont);\r
8153 }\r
8154 \r
8155 \r
8156 int\r
8157 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8158            OVERLAPPED *ovl)\r
8159 {\r
8160   int ok, err;\r
8161 \r
8162   /* [AS]  */\r
8163   if( count <= 0 ) {\r
8164     if (appData.debugMode) {\r
8165       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8166     }\r
8167 \r
8168     return ERROR_INVALID_USER_BUFFER;\r
8169   }\r
8170 \r
8171   ResetEvent(ovl->hEvent);\r
8172   ovl->Offset = ovl->OffsetHigh = 0;\r
8173   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8174   if (ok) {\r
8175     err = NO_ERROR;\r
8176   } else {\r
8177     err = GetLastError();\r
8178     if (err == ERROR_IO_PENDING) {\r
8179       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8180       if (ok)\r
8181         err = NO_ERROR;\r
8182       else\r
8183         err = GetLastError();\r
8184     }\r
8185   }\r
8186   return err;\r
8187 }\r
8188 \r
8189 int\r
8190 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8191             OVERLAPPED *ovl)\r
8192 {\r
8193   int ok, err;\r
8194 \r
8195   ResetEvent(ovl->hEvent);\r
8196   ovl->Offset = ovl->OffsetHigh = 0;\r
8197   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8198   if (ok) {\r
8199     err = NO_ERROR;\r
8200   } else {\r
8201     err = GetLastError();\r
8202     if (err == ERROR_IO_PENDING) {\r
8203       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8204       if (ok)\r
8205         err = NO_ERROR;\r
8206       else\r
8207         err = GetLastError();\r
8208     }\r
8209   }\r
8210   return err;\r
8211 }\r
8212 \r
8213 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8214 void CheckForInputBufferFull( InputSource * is )\r
8215 {\r
8216     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8217         /* Look for end of line */\r
8218         char * p = is->buf;\r
8219         \r
8220         while( p < is->next && *p != '\n' ) {\r
8221             p++;\r
8222         }\r
8223 \r
8224         if( p >= is->next ) {\r
8225             if (appData.debugMode) {\r
8226                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8227             }\r
8228 \r
8229             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8230             is->count = (DWORD) -1;\r
8231             is->next = is->buf;\r
8232         }\r
8233     }\r
8234 }\r
8235 \r
8236 DWORD\r
8237 InputThread(LPVOID arg)\r
8238 {\r
8239   InputSource *is;\r
8240   OVERLAPPED ovl;\r
8241 \r
8242   is = (InputSource *) arg;\r
8243   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8244   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8245   while (is->hThread != NULL) {\r
8246     is->error = DoReadFile(is->hFile, is->next,\r
8247                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8248                            &is->count, &ovl);\r
8249     if (is->error == NO_ERROR) {\r
8250       is->next += is->count;\r
8251     } else {\r
8252       if (is->error == ERROR_BROKEN_PIPE) {\r
8253         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8254         is->count = 0;\r
8255       } else {\r
8256         is->count = (DWORD) -1;\r
8257         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8258         break; \r
8259       }\r
8260     }\r
8261 \r
8262     CheckForInputBufferFull( is );\r
8263 \r
8264     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8265 \r
8266     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8267 \r
8268     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8269   }\r
8270 \r
8271   CloseHandle(ovl.hEvent);\r
8272   CloseHandle(is->hFile);\r
8273 \r
8274   if (appData.debugMode) {\r
8275     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8276   }\r
8277 \r
8278   return 0;\r
8279 }\r
8280 \r
8281 \r
8282 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8283 DWORD\r
8284 NonOvlInputThread(LPVOID arg)\r
8285 {\r
8286   InputSource *is;\r
8287   char *p, *q;\r
8288   int i;\r
8289   char prev;\r
8290 \r
8291   is = (InputSource *) arg;\r
8292   while (is->hThread != NULL) {\r
8293     is->error = ReadFile(is->hFile, is->next,\r
8294                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8295                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8296     if (is->error == NO_ERROR) {\r
8297       /* Change CRLF to LF */\r
8298       if (is->next > is->buf) {\r
8299         p = is->next - 1;\r
8300         i = is->count + 1;\r
8301       } else {\r
8302         p = is->next;\r
8303         i = is->count;\r
8304       }\r
8305       q = p;\r
8306       prev = NULLCHAR;\r
8307       while (i > 0) {\r
8308         if (prev == '\r' && *p == '\n') {\r
8309           *(q-1) = '\n';\r
8310           is->count--;\r
8311         } else { \r
8312           *q++ = *p;\r
8313         }\r
8314         prev = *p++;\r
8315         i--;\r
8316       }\r
8317       *q = NULLCHAR;\r
8318       is->next = q;\r
8319     } else {\r
8320       if (is->error == ERROR_BROKEN_PIPE) {\r
8321         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8322         is->count = 0; \r
8323       } else {\r
8324         is->count = (DWORD) -1;\r
8325       }\r
8326     }\r
8327 \r
8328     CheckForInputBufferFull( is );\r
8329 \r
8330     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8331 \r
8332     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8333 \r
8334     if (is->count < 0) break;  /* Quit on error */\r
8335   }\r
8336   CloseHandle(is->hFile);\r
8337   return 0;\r
8338 }\r
8339 \r
8340 DWORD\r
8341 SocketInputThread(LPVOID arg)\r
8342 {\r
8343   InputSource *is;\r
8344 \r
8345   is = (InputSource *) arg;\r
8346   while (is->hThread != NULL) {\r
8347     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8348     if ((int)is->count == SOCKET_ERROR) {\r
8349       is->count = (DWORD) -1;\r
8350       is->error = WSAGetLastError();\r
8351     } else {\r
8352       is->error = NO_ERROR;\r
8353       is->next += is->count;\r
8354       if (is->count == 0 && is->second == is) {\r
8355         /* End of file on stderr; quit with no message */\r
8356         break;\r
8357       }\r
8358     }\r
8359     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8360 \r
8361     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8362 \r
8363     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8364   }\r
8365   return 0;\r
8366 }\r
8367 \r
8368 VOID\r
8369 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8370 {\r
8371   InputSource *is;\r
8372 \r
8373   is = (InputSource *) lParam;\r
8374   if (is->lineByLine) {\r
8375     /* Feed in lines one by one */\r
8376     char *p = is->buf;\r
8377     char *q = p;\r
8378     while (q < is->next) {\r
8379       if (*q++ == '\n') {\r
8380         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8381         p = q;\r
8382       }\r
8383     }\r
8384     \r
8385     /* Move any partial line to the start of the buffer */\r
8386     q = is->buf;\r
8387     while (p < is->next) {\r
8388       *q++ = *p++;\r
8389     }\r
8390     is->next = q;\r
8391 \r
8392     if (is->error != NO_ERROR || is->count == 0) {\r
8393       /* Notify backend of the error.  Note: If there was a partial\r
8394          line at the end, it is not flushed through. */\r
8395       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8396     }\r
8397   } else {\r
8398     /* Feed in the whole chunk of input at once */\r
8399     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8400     is->next = is->buf;\r
8401   }\r
8402 }\r
8403 \r
8404 /*---------------------------------------------------------------------------*\\r
8405  *\r
8406  *  Menu enables. Used when setting various modes.\r
8407  *\r
8408 \*---------------------------------------------------------------------------*/\r
8409 \r
8410 typedef struct {\r
8411   int item;\r
8412   int flags;\r
8413 } Enables;\r
8414 \r
8415 VOID\r
8416 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8417 {\r
8418   while (enab->item > 0) {\r
8419     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8420     enab++;\r
8421   }\r
8422 }\r
8423 \r
8424 Enables gnuEnables[] = {\r
8425   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8426   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8427   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8428   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8429   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8430   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8431   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8432   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8433   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8434   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8435   { -1, -1 }\r
8436 };\r
8437 \r
8438 Enables icsEnables[] = {\r
8439   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8440   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8441   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8442   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8443   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8444   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8445   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8446   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8447   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8448   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8449   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8450   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8451   { -1, -1 }\r
8452 };\r
8453 \r
8454 #ifdef ZIPPY\r
8455 Enables zippyEnables[] = {\r
8456   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8457   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8458   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8459   { -1, -1 }\r
8460 };\r
8461 #endif\r
8462 \r
8463 Enables ncpEnables[] = {\r
8464   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8465   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8466   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8467   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8468   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8469   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8470   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8471   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8472   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8473   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8474   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8475   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8476   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8477   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8478   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8479   { -1, -1 }\r
8480 };\r
8481 \r
8482 Enables trainingOnEnables[] = {\r
8483   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8484   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8485   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8486   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8487   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8488   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8489   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8490   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8491   { -1, -1 }\r
8492 };\r
8493 \r
8494 Enables trainingOffEnables[] = {\r
8495   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8496   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8497   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8498   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8499   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8500   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8501   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8502   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8503   { -1, -1 }\r
8504 };\r
8505 \r
8506 /* These modify either ncpEnables or gnuEnables */\r
8507 Enables cmailEnables[] = {\r
8508   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8509   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8510   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8511   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8513   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8515   { -1, -1 }\r
8516 };\r
8517 \r
8518 Enables machineThinkingEnables[] = {\r
8519   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8520   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8521   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8522   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8523   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8529   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8531   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8532   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8533   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8534   { -1, -1 }\r
8535 };\r
8536 \r
8537 Enables userThinkingEnables[] = {\r
8538   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8539   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8540   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8541   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8542   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8543   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8544   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8545   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8546   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8547   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8548   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8549   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8550   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8551   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8552   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8553   { -1, -1 }\r
8554 };\r
8555 \r
8556 /*---------------------------------------------------------------------------*\\r
8557  *\r
8558  *  Front-end interface functions exported by XBoard.\r
8559  *  Functions appear in same order as prototypes in frontend.h.\r
8560  * \r
8561 \*---------------------------------------------------------------------------*/\r
8562 VOID\r
8563 ModeHighlight()\r
8564 {\r
8565   static UINT prevChecked = 0;\r
8566   static int prevPausing = 0;\r
8567   UINT nowChecked;\r
8568 \r
8569   if (pausing != prevPausing) {\r
8570     prevPausing = pausing;\r
8571     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8572                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8573     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8574   }\r
8575 \r
8576   switch (gameMode) {\r
8577   case BeginningOfGame:\r
8578     if (appData.icsActive)\r
8579       nowChecked = IDM_IcsClient;\r
8580     else if (appData.noChessProgram)\r
8581       nowChecked = IDM_EditGame;\r
8582     else\r
8583       nowChecked = IDM_MachineBlack;\r
8584     break;\r
8585   case MachinePlaysBlack:\r
8586     nowChecked = IDM_MachineBlack;\r
8587     break;\r
8588   case MachinePlaysWhite:\r
8589     nowChecked = IDM_MachineWhite;\r
8590     break;\r
8591   case TwoMachinesPlay:\r
8592     nowChecked = IDM_TwoMachines;\r
8593     break;\r
8594   case AnalyzeMode:\r
8595     nowChecked = IDM_AnalysisMode;\r
8596     break;\r
8597   case AnalyzeFile:\r
8598     nowChecked = IDM_AnalyzeFile;\r
8599     break;\r
8600   case EditGame:\r
8601     nowChecked = IDM_EditGame;\r
8602     break;\r
8603   case PlayFromGameFile:\r
8604     nowChecked = IDM_LoadGame;\r
8605     break;\r
8606   case EditPosition:\r
8607     nowChecked = IDM_EditPosition;\r
8608     break;\r
8609   case Training:\r
8610     nowChecked = IDM_Training;\r
8611     break;\r
8612   case IcsPlayingWhite:\r
8613   case IcsPlayingBlack:\r
8614   case IcsObserving:\r
8615   case IcsIdle:\r
8616     nowChecked = IDM_IcsClient;\r
8617     break;\r
8618   default:\r
8619   case EndOfGame:\r
8620     nowChecked = 0;\r
8621     break;\r
8622   }\r
8623   if (prevChecked != 0)\r
8624     (void) CheckMenuItem(GetMenu(hwndMain),\r
8625                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8626   if (nowChecked != 0)\r
8627     (void) CheckMenuItem(GetMenu(hwndMain),\r
8628                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8629 \r
8630   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8631     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8632                           MF_BYCOMMAND|MF_ENABLED);\r
8633   } else {\r
8634     (void) EnableMenuItem(GetMenu(hwndMain), \r
8635                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8636   }\r
8637 \r
8638   prevChecked = nowChecked;\r
8639 }\r
8640 \r
8641 VOID\r
8642 SetICSMode()\r
8643 {\r
8644   HMENU hmenu = GetMenu(hwndMain);\r
8645   SetMenuEnables(hmenu, icsEnables);\r
8646   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8647     MF_BYPOSITION|MF_ENABLED);\r
8648 #ifdef ZIPPY\r
8649   if (appData.zippyPlay) {\r
8650     SetMenuEnables(hmenu, zippyEnables);\r
8651   }\r
8652 #endif\r
8653 }\r
8654 \r
8655 VOID\r
8656 SetGNUMode()\r
8657 {\r
8658   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8659 }\r
8660 \r
8661 VOID\r
8662 SetNCPMode()\r
8663 {\r
8664   HMENU hmenu = GetMenu(hwndMain);\r
8665   SetMenuEnables(hmenu, ncpEnables);\r
8666   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8667     MF_BYPOSITION|MF_GRAYED);\r
8668     DrawMenuBar(hwndMain);\r
8669 }\r
8670 \r
8671 VOID\r
8672 SetCmailMode()\r
8673 {\r
8674   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8675 }\r
8676 \r
8677 VOID \r
8678 SetTrainingModeOn()\r
8679 {\r
8680   int i;\r
8681   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8682   for (i = 0; i < N_BUTTONS; i++) {\r
8683     if (buttonDesc[i].hwnd != NULL)\r
8684       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8685   }\r
8686   CommentPopDown();\r
8687 }\r
8688 \r
8689 VOID SetTrainingModeOff()\r
8690 {\r
8691   int i;\r
8692   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8693   for (i = 0; i < N_BUTTONS; i++) {\r
8694     if (buttonDesc[i].hwnd != NULL)\r
8695       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8696   }\r
8697 }\r
8698 \r
8699 \r
8700 VOID\r
8701 SetUserThinkingEnables()\r
8702 {\r
8703   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8704 }\r
8705 \r
8706 VOID\r
8707 SetMachineThinkingEnables()\r
8708 {\r
8709   HMENU hMenu = GetMenu(hwndMain);\r
8710   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8711 \r
8712   SetMenuEnables(hMenu, machineThinkingEnables);\r
8713 \r
8714   if (gameMode == MachinePlaysBlack) {\r
8715     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8716   } else if (gameMode == MachinePlaysWhite) {\r
8717     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8718   } else if (gameMode == TwoMachinesPlay) {\r
8719     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8720   }\r
8721 }\r
8722 \r
8723 \r
8724 VOID\r
8725 DisplayTitle(char *str)\r
8726 {\r
8727   char title[MSG_SIZ], *host;\r
8728   if (str[0] != NULLCHAR) {\r
8729     strcpy(title, str);\r
8730   } else if (appData.icsActive) {\r
8731     if (appData.icsCommPort[0] != NULLCHAR)\r
8732       host = "ICS";\r
8733     else \r
8734       host = appData.icsHost;\r
8735     sprintf(title, "%s: %s", szTitle, host);\r
8736   } else if (appData.noChessProgram) {\r
8737     strcpy(title, szTitle);\r
8738   } else {\r
8739     strcpy(title, szTitle);\r
8740     strcat(title, ": ");\r
8741     strcat(title, first.tidy);\r
8742   }\r
8743   SetWindowText(hwndMain, title);\r
8744 }\r
8745 \r
8746 \r
8747 VOID\r
8748 DisplayMessage(char *str1, char *str2)\r
8749 {\r
8750   HDC hdc;\r
8751   HFONT oldFont;\r
8752   int remain = MESSAGE_TEXT_MAX - 1;\r
8753   int len;\r
8754 \r
8755   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8756   messageText[0] = NULLCHAR;\r
8757   if (*str1) {\r
8758     len = strlen(str1);\r
8759     if (len > remain) len = remain;\r
8760     strncpy(messageText, str1, len);\r
8761     messageText[len] = NULLCHAR;\r
8762     remain -= len;\r
8763   }\r
8764   if (*str2 && remain >= 2) {\r
8765     if (*str1) {\r
8766       strcat(messageText, "  ");\r
8767       remain -= 2;\r
8768     }\r
8769     len = strlen(str2);\r
8770     if (len > remain) len = remain;\r
8771     strncat(messageText, str2, len);\r
8772   }\r
8773   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8774 \r
8775   if (IsIconic(hwndMain)) return;\r
8776   hdc = GetDC(hwndMain);\r
8777   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8778   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8779              &messageRect, messageText, strlen(messageText), NULL);\r
8780   (void) SelectObject(hdc, oldFont);\r
8781   (void) ReleaseDC(hwndMain, hdc);\r
8782 }\r
8783 \r
8784 VOID\r
8785 DisplayError(char *str, int error)\r
8786 {\r
8787   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8788   int len;\r
8789 \r
8790   if (error == 0) {\r
8791     strcpy(buf, str);\r
8792   } else {\r
8793     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8794                         NULL, error, LANG_NEUTRAL,\r
8795                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8796     if (len > 0) {\r
8797       sprintf(buf, "%s:\n%s", str, buf2);\r
8798     } else {\r
8799       ErrorMap *em = errmap;\r
8800       while (em->err != 0 && em->err != error) em++;\r
8801       if (em->err != 0) {\r
8802         sprintf(buf, "%s:\n%s", str, em->msg);\r
8803       } else {\r
8804         sprintf(buf, "%s:\nError code %d", str, error);\r
8805       }\r
8806     }\r
8807   }\r
8808   \r
8809   ErrorPopUp("Error", buf);\r
8810 }\r
8811 \r
8812 \r
8813 VOID\r
8814 DisplayMoveError(char *str)\r
8815 {\r
8816   fromX = fromY = -1;\r
8817   ClearHighlights();\r
8818   DrawPosition(FALSE, NULL);\r
8819   if (appData.popupMoveErrors) {\r
8820     ErrorPopUp("Error", str);\r
8821   } else {\r
8822     DisplayMessage(str, "");\r
8823     moveErrorMessageUp = TRUE;\r
8824   }\r
8825 }\r
8826 \r
8827 VOID\r
8828 DisplayFatalError(char *str, int error, int exitStatus)\r
8829 {\r
8830   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8831   int len;\r
8832   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8833 \r
8834   if (error != 0) {\r
8835     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8836                         NULL, error, LANG_NEUTRAL,\r
8837                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8838     if (len > 0) {\r
8839       sprintf(buf, "%s:\n%s", str, buf2);\r
8840     } else {\r
8841       ErrorMap *em = errmap;\r
8842       while (em->err != 0 && em->err != error) em++;\r
8843       if (em->err != 0) {\r
8844         sprintf(buf, "%s:\n%s", str, em->msg);\r
8845       } else {\r
8846         sprintf(buf, "%s:\nError code %d", str, error);\r
8847       }\r
8848     }\r
8849     str = buf;\r
8850   }\r
8851   if (appData.debugMode) {\r
8852     fprintf(debugFP, "%s: %s\n", label, str);\r
8853   }\r
8854   if (appData.popupExitMessage) {\r
8855     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8856                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8857   }\r
8858   ExitEvent(exitStatus);\r
8859 }\r
8860 \r
8861 \r
8862 VOID\r
8863 DisplayInformation(char *str)\r
8864 {\r
8865   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8866 }\r
8867 \r
8868 \r
8869 VOID\r
8870 DisplayNote(char *str)\r
8871 {\r
8872   ErrorPopUp("Note", str);\r
8873 }\r
8874 \r
8875 \r
8876 typedef struct {\r
8877   char *title, *question, *replyPrefix;\r
8878   ProcRef pr;\r
8879 } QuestionParams;\r
8880 \r
8881 LRESULT CALLBACK\r
8882 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8883 {\r
8884   static QuestionParams *qp;\r
8885   char reply[MSG_SIZ];\r
8886   int len, err;\r
8887 \r
8888   switch (message) {\r
8889   case WM_INITDIALOG:\r
8890     qp = (QuestionParams *) lParam;\r
8891     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8892     SetWindowText(hDlg, qp->title);\r
8893     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8894     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8895     return FALSE;\r
8896 \r
8897   case WM_COMMAND:\r
8898     switch (LOWORD(wParam)) {\r
8899     case IDOK:\r
8900       strcpy(reply, qp->replyPrefix);\r
8901       if (*reply) strcat(reply, " ");\r
8902       len = strlen(reply);\r
8903       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8904       strcat(reply, "\n");\r
8905       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8906       EndDialog(hDlg, TRUE);\r
8907       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8908       return TRUE;\r
8909     case IDCANCEL:\r
8910       EndDialog(hDlg, FALSE);\r
8911       return TRUE;\r
8912     default:\r
8913       break;\r
8914     }\r
8915     break;\r
8916   }\r
8917   return FALSE;\r
8918 }\r
8919 \r
8920 VOID\r
8921 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8922 {\r
8923     QuestionParams qp;\r
8924     FARPROC lpProc;\r
8925     \r
8926     qp.title = title;\r
8927     qp.question = question;\r
8928     qp.replyPrefix = replyPrefix;\r
8929     qp.pr = pr;\r
8930     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8931     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8932       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8933     FreeProcInstance(lpProc);\r
8934 }\r
8935 \r
8936 /* [AS] Pick FRC position */\r
8937 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8938 {\r
8939     static int * lpIndexFRC;\r
8940     BOOL index_is_ok;\r
8941     char buf[16];\r
8942 \r
8943     switch( message )\r
8944     {\r
8945     case WM_INITDIALOG:\r
8946         lpIndexFRC = (int *) lParam;\r
8947 \r
8948         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8949 \r
8950         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8951         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8952         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8953         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8954 \r
8955         break;\r
8956 \r
8957     case WM_COMMAND:\r
8958         switch( LOWORD(wParam) ) {\r
8959         case IDOK:\r
8960             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8961             EndDialog( hDlg, 0 );\r
8962             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8963             return TRUE;\r
8964         case IDCANCEL:\r
8965             EndDialog( hDlg, 1 );   \r
8966             return TRUE;\r
8967         case IDC_NFG_Edit:\r
8968             if( HIWORD(wParam) == EN_CHANGE ) {\r
8969                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8970 \r
8971                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8972             }\r
8973             return TRUE;\r
8974         case IDC_NFG_Random:\r
8975             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8976             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8977             return TRUE;\r
8978         }\r
8979 \r
8980         break;\r
8981     }\r
8982 \r
8983     return FALSE;\r
8984 }\r
8985 \r
8986 int NewGameFRC()\r
8987 {\r
8988     int result;\r
8989     int index = appData.defaultFrcPosition;\r
8990     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8991 \r
8992     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8993 \r
8994     if( result == 0 ) {\r
8995         appData.defaultFrcPosition = index;\r
8996     }\r
8997 \r
8998     return result;\r
8999 }\r
9000 \r
9001 /* [AS] Game list options */\r
9002 typedef struct {\r
9003     char id;\r
9004     char * name;\r
9005 } GLT_Item;\r
9006 \r
9007 static GLT_Item GLT_ItemInfo[] = {\r
9008     { GLT_EVENT,      "Event" },\r
9009     { GLT_SITE,       "Site" },\r
9010     { GLT_DATE,       "Date" },\r
9011     { GLT_ROUND,      "Round" },\r
9012     { GLT_PLAYERS,    "Players" },\r
9013     { GLT_RESULT,     "Result" },\r
9014     { GLT_WHITE_ELO,  "White Rating" },\r
9015     { GLT_BLACK_ELO,  "Black Rating" },\r
9016     { GLT_TIME_CONTROL,"Time Control" },\r
9017     { GLT_VARIANT,    "Variant" },\r
9018     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9019     { 0, 0 }\r
9020 };\r
9021 \r
9022 const char * GLT_FindItem( char id )\r
9023 {\r
9024     const char * result = 0;\r
9025 \r
9026     GLT_Item * list = GLT_ItemInfo;\r
9027 \r
9028     while( list->id != 0 ) {\r
9029         if( list->id == id ) {\r
9030             result = list->name;\r
9031             break;\r
9032         }\r
9033 \r
9034         list++;\r
9035     }\r
9036 \r
9037     return result;\r
9038 }\r
9039 \r
9040 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9041 {\r
9042     const char * name = GLT_FindItem( id );\r
9043 \r
9044     if( name != 0 ) {\r
9045         if( index >= 0 ) {\r
9046             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9047         }\r
9048         else {\r
9049             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9050         }\r
9051     }\r
9052 }\r
9053 \r
9054 void GLT_TagsToList( HWND hDlg, char * tags )\r
9055 {\r
9056     char * pc = tags;\r
9057 \r
9058     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9059 \r
9060     while( *pc ) {\r
9061         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9062         pc++;\r
9063     }\r
9064 \r
9065     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9066 \r
9067     pc = GLT_ALL_TAGS;\r
9068 \r
9069     while( *pc ) {\r
9070         if( strchr( tags, *pc ) == 0 ) {\r
9071             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9072         }\r
9073         pc++;\r
9074     }\r
9075 \r
9076     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9077 }\r
9078 \r
9079 char GLT_ListItemToTag( HWND hDlg, int index )\r
9080 {\r
9081     char result = '\0';\r
9082     char name[128];\r
9083 \r
9084     GLT_Item * list = GLT_ItemInfo;\r
9085 \r
9086     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9087         while( list->id != 0 ) {\r
9088             if( strcmp( list->name, name ) == 0 ) {\r
9089                 result = list->id;\r
9090                 break;\r
9091             }\r
9092 \r
9093             list++;\r
9094         }\r
9095     }\r
9096 \r
9097     return result;\r
9098 }\r
9099 \r
9100 void GLT_MoveSelection( HWND hDlg, int delta )\r
9101 {\r
9102     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9103     int idx2 = idx1 + delta;\r
9104     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9105 \r
9106     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9107         char buf[128];\r
9108 \r
9109         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9110         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9111         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9112         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9113     }\r
9114 }\r
9115 \r
9116 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9117 {\r
9118     static char glt[64];\r
9119     static char * lpUserGLT;\r
9120 \r
9121     switch( message )\r
9122     {\r
9123     case WM_INITDIALOG:\r
9124         lpUserGLT = (char *) lParam;\r
9125         \r
9126         strcpy( glt, lpUserGLT );\r
9127 \r
9128         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9129 \r
9130         /* Initialize list */\r
9131         GLT_TagsToList( hDlg, glt );\r
9132 \r
9133         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9134 \r
9135         break;\r
9136 \r
9137     case WM_COMMAND:\r
9138         switch( LOWORD(wParam) ) {\r
9139         case IDOK:\r
9140             {\r
9141                 char * pc = lpUserGLT;\r
9142                 int idx = 0;\r
9143                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9144                 char id;\r
9145 \r
9146                 do {\r
9147                     id = GLT_ListItemToTag( hDlg, idx );\r
9148 \r
9149                     *pc++ = id;\r
9150                     idx++;\r
9151                 } while( id != '\0' );\r
9152             }\r
9153             EndDialog( hDlg, 0 );\r
9154             return TRUE;\r
9155         case IDCANCEL:\r
9156             EndDialog( hDlg, 1 );\r
9157             return TRUE;\r
9158 \r
9159         case IDC_GLT_Default:\r
9160             strcpy( glt, GLT_DEFAULT_TAGS );\r
9161             GLT_TagsToList( hDlg, glt );\r
9162             return TRUE;\r
9163 \r
9164         case IDC_GLT_Restore:\r
9165             strcpy( glt, lpUserGLT );\r
9166             GLT_TagsToList( hDlg, glt );\r
9167             return TRUE;\r
9168 \r
9169         case IDC_GLT_Up:\r
9170             GLT_MoveSelection( hDlg, -1 );\r
9171             return TRUE;\r
9172 \r
9173         case IDC_GLT_Down:\r
9174             GLT_MoveSelection( hDlg, +1 );\r
9175             return TRUE;\r
9176         }\r
9177 \r
9178         break;\r
9179     }\r
9180 \r
9181     return FALSE;\r
9182 }\r
9183 \r
9184 int GameListOptions()\r
9185 {\r
9186     char glt[64];\r
9187     int result;\r
9188     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9189 \r
9190     strcpy( glt, appData.gameListTags );\r
9191 \r
9192     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9193 \r
9194     if( result == 0 ) {\r
9195         /* [AS] Memory leak here! */\r
9196         appData.gameListTags = strdup( glt ); \r
9197     }\r
9198 \r
9199     return result;\r
9200 }\r
9201 \r
9202 \r
9203 VOID\r
9204 DisplayIcsInteractionTitle(char *str)\r
9205 {\r
9206   char consoleTitle[MSG_SIZ];\r
9207 \r
9208   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9209   SetWindowText(hwndConsole, consoleTitle);\r
9210 }\r
9211 \r
9212 void\r
9213 DrawPosition(int fullRedraw, Board board)\r
9214 {\r
9215   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9216 }\r
9217 \r
9218 \r
9219 VOID\r
9220 ResetFrontEnd()\r
9221 {\r
9222   fromX = fromY = -1;\r
9223   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9224     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9225     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9226     dragInfo.lastpos = dragInfo.pos;\r
9227     dragInfo.start.x = dragInfo.start.y = -1;\r
9228     dragInfo.from = dragInfo.start;\r
9229     ReleaseCapture();\r
9230     DrawPosition(TRUE, NULL);\r
9231   }\r
9232 }\r
9233 \r
9234 \r
9235 VOID\r
9236 CommentPopUp(char *title, char *str)\r
9237 {\r
9238   HWND hwnd = GetActiveWindow();\r
9239   EitherCommentPopUp(0, title, str, FALSE);\r
9240   SetActiveWindow(hwnd);\r
9241 }\r
9242 \r
9243 VOID\r
9244 CommentPopDown(void)\r
9245 {\r
9246   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9247   if (commentDialog) {\r
9248     ShowWindow(commentDialog, SW_HIDE);\r
9249   }\r
9250   commentDialogUp = FALSE;\r
9251 }\r
9252 \r
9253 VOID\r
9254 EditCommentPopUp(int index, char *title, char *str)\r
9255 {\r
9256   EitherCommentPopUp(index, title, str, TRUE);\r
9257 }\r
9258 \r
9259 \r
9260 VOID\r
9261 RingBell()\r
9262 {\r
9263   MyPlaySound(&sounds[(int)SoundMove]);\r
9264 }\r
9265 \r
9266 VOID PlayIcsWinSound()\r
9267 {\r
9268   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9269 }\r
9270 \r
9271 VOID PlayIcsLossSound()\r
9272 {\r
9273   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9274 }\r
9275 \r
9276 VOID PlayIcsDrawSound()\r
9277 {\r
9278   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9279 }\r
9280 \r
9281 VOID PlayIcsUnfinishedSound()\r
9282 {\r
9283   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9284 }\r
9285 \r
9286 VOID\r
9287 PlayAlarmSound()\r
9288 {\r
9289   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9290 }\r
9291 \r
9292 \r
9293 VOID\r
9294 EchoOn()\r
9295 {\r
9296   HWND hInput;\r
9297   consoleEcho = TRUE;\r
9298   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9299   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9300   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9301 }\r
9302 \r
9303 \r
9304 VOID\r
9305 EchoOff()\r
9306 {\r
9307   CHARFORMAT cf;\r
9308   HWND hInput;\r
9309   consoleEcho = FALSE;\r
9310   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9311   /* This works OK: set text and background both to the same color */\r
9312   cf = consoleCF;\r
9313   cf.crTextColor = COLOR_ECHOOFF;\r
9314   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9315   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9316 }\r
9317 \r
9318 /* No Raw()...? */\r
9319 \r
9320 void Colorize(ColorClass cc, int continuation)\r
9321 {\r
9322   currentColorClass = cc;\r
9323   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9324   consoleCF.crTextColor = textAttribs[cc].color;\r
9325   consoleCF.dwEffects = textAttribs[cc].effects;\r
9326   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9327 }\r
9328 \r
9329 char *\r
9330 UserName()\r
9331 {\r
9332   static char buf[MSG_SIZ];\r
9333   DWORD bufsiz = MSG_SIZ;\r
9334 \r
9335   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9336         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9337   }\r
9338   if (!GetUserName(buf, &bufsiz)) {\r
9339     /*DisplayError("Error getting user name", GetLastError());*/\r
9340     strcpy(buf, "User");\r
9341   }\r
9342   return buf;\r
9343 }\r
9344 \r
9345 char *\r
9346 HostName()\r
9347 {\r
9348   static char buf[MSG_SIZ];\r
9349   DWORD bufsiz = MSG_SIZ;\r
9350 \r
9351   if (!GetComputerName(buf, &bufsiz)) {\r
9352     /*DisplayError("Error getting host name", GetLastError());*/\r
9353     strcpy(buf, "Unknown");\r
9354   }\r
9355   return buf;\r
9356 }\r
9357 \r
9358 \r
9359 int\r
9360 ClockTimerRunning()\r
9361 {\r
9362   return clockTimerEvent != 0;\r
9363 }\r
9364 \r
9365 int\r
9366 StopClockTimer()\r
9367 {\r
9368   if (clockTimerEvent == 0) return FALSE;\r
9369   KillTimer(hwndMain, clockTimerEvent);\r
9370   clockTimerEvent = 0;\r
9371   return TRUE;\r
9372 }\r
9373 \r
9374 void\r
9375 StartClockTimer(long millisec)\r
9376 {\r
9377   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9378                              (UINT) millisec, NULL);\r
9379 }\r
9380 \r
9381 void\r
9382 DisplayWhiteClock(long timeRemaining, int highlight)\r
9383 {\r
9384   HDC hdc;\r
9385   hdc = GetDC(hwndMain);\r
9386   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9387 \r
9388   if (!IsIconic(hwndMain)) {\r
9389     DisplayAClock(hdc, timeRemaining, highlight, \r
9390                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9391   }\r
9392   if (highlight && iconCurrent == iconBlack) {\r
9393     iconCurrent = iconWhite;\r
9394     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9395     if (IsIconic(hwndMain)) {\r
9396       DrawIcon(hdc, 2, 2, iconCurrent);\r
9397     }\r
9398   }\r
9399   (void) ReleaseDC(hwndMain, hdc);\r
9400   if (hwndConsole)\r
9401     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9402 }\r
9403 \r
9404 void\r
9405 DisplayBlackClock(long timeRemaining, int highlight)\r
9406 {\r
9407   HDC hdc;\r
9408   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9409 \r
9410   hdc = GetDC(hwndMain);\r
9411   if (!IsIconic(hwndMain)) {\r
9412     DisplayAClock(hdc, timeRemaining, highlight, \r
9413                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9414   }\r
9415   if (highlight && iconCurrent == iconWhite) {\r
9416     iconCurrent = iconBlack;\r
9417     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9418     if (IsIconic(hwndMain)) {\r
9419       DrawIcon(hdc, 2, 2, iconCurrent);\r
9420     }\r
9421   }\r
9422   (void) ReleaseDC(hwndMain, hdc);\r
9423   if (hwndConsole)\r
9424     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9425 }\r
9426 \r
9427 \r
9428 int\r
9429 LoadGameTimerRunning()\r
9430 {\r
9431   return loadGameTimerEvent != 0;\r
9432 }\r
9433 \r
9434 int\r
9435 StopLoadGameTimer()\r
9436 {\r
9437   if (loadGameTimerEvent == 0) return FALSE;\r
9438   KillTimer(hwndMain, loadGameTimerEvent);\r
9439   loadGameTimerEvent = 0;\r
9440   return TRUE;\r
9441 }\r
9442 \r
9443 void\r
9444 StartLoadGameTimer(long millisec)\r
9445 {\r
9446   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9447                                 (UINT) millisec, NULL);\r
9448 }\r
9449 \r
9450 void\r
9451 AutoSaveGame()\r
9452 {\r
9453   char *defName;\r
9454   FILE *f;\r
9455   char fileTitle[MSG_SIZ];\r
9456 \r
9457   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9458   f = OpenFileDialog(hwndMain, "a", defName,\r
9459                      appData.oldSaveStyle ? "gam" : "pgn",\r
9460                      GAME_FILT, \r
9461                      "Save Game to File", NULL, fileTitle, NULL);\r
9462   if (f != NULL) {\r
9463     SaveGame(f, 0, "");\r
9464     fclose(f);\r
9465   }\r
9466 }\r
9467 \r
9468 \r
9469 void\r
9470 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9471 {\r
9472   if (delayedTimerEvent != 0) {\r
9473     if (appData.debugMode) {\r
9474       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9475     }\r
9476     KillTimer(hwndMain, delayedTimerEvent);\r
9477     delayedTimerEvent = 0;\r
9478     delayedTimerCallback();\r
9479   }\r
9480   delayedTimerCallback = cb;\r
9481   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9482                                 (UINT) millisec, NULL);\r
9483 }\r
9484 \r
9485 DelayedEventCallback\r
9486 GetDelayedEvent()\r
9487 {\r
9488   if (delayedTimerEvent) {\r
9489     return delayedTimerCallback;\r
9490   } else {\r
9491     return NULL;\r
9492   }\r
9493 }\r
9494 \r
9495 void\r
9496 CancelDelayedEvent()\r
9497 {\r
9498   if (delayedTimerEvent) {\r
9499     KillTimer(hwndMain, delayedTimerEvent);\r
9500     delayedTimerEvent = 0;\r
9501   }\r
9502 }\r
9503 \r
9504 DWORD GetWin32Priority(int nice)\r
9505 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9506 /*\r
9507 REALTIME_PRIORITY_CLASS     0x00000100\r
9508 HIGH_PRIORITY_CLASS         0x00000080\r
9509 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9510 NORMAL_PRIORITY_CLASS       0x00000020\r
9511 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9512 IDLE_PRIORITY_CLASS         0x00000040\r
9513 */\r
9514         if (nice < -15) return 0x00000080;\r
9515         if (nice < 0)   return 0x00008000;\r
9516         if (nice == 0)  return 0x00000020;\r
9517         if (nice < 15)  return 0x00004000;\r
9518         return 0x00000040;\r
9519 }\r
9520 \r
9521 /* Start a child process running the given program.\r
9522    The process's standard output can be read from "from", and its\r
9523    standard input can be written to "to".\r
9524    Exit with fatal error if anything goes wrong.\r
9525    Returns an opaque pointer that can be used to destroy the process\r
9526    later.\r
9527 */\r
9528 int\r
9529 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9530 {\r
9531 #define BUFSIZE 4096\r
9532 \r
9533   HANDLE hChildStdinRd, hChildStdinWr,\r
9534     hChildStdoutRd, hChildStdoutWr;\r
9535   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9536   SECURITY_ATTRIBUTES saAttr;\r
9537   BOOL fSuccess;\r
9538   PROCESS_INFORMATION piProcInfo;\r
9539   STARTUPINFO siStartInfo;\r
9540   ChildProc *cp;\r
9541   char buf[MSG_SIZ];\r
9542   DWORD err;\r
9543 \r
9544   if (appData.debugMode) {\r
9545     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9546   }\r
9547 \r
9548   *pr = NoProc;\r
9549 \r
9550   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9551   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9552   saAttr.bInheritHandle = TRUE;\r
9553   saAttr.lpSecurityDescriptor = NULL;\r
9554 \r
9555   /*\r
9556    * The steps for redirecting child's STDOUT:\r
9557    *     1. Create anonymous pipe to be STDOUT for child.\r
9558    *     2. Create a noninheritable duplicate of read handle,\r
9559    *         and close the inheritable read handle.\r
9560    */\r
9561 \r
9562   /* Create a pipe for the child's STDOUT. */\r
9563   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9564     return GetLastError();\r
9565   }\r
9566 \r
9567   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9568   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9569                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9570                              FALSE,     /* not inherited */\r
9571                              DUPLICATE_SAME_ACCESS);\r
9572   if (! fSuccess) {\r
9573     return GetLastError();\r
9574   }\r
9575   CloseHandle(hChildStdoutRd);\r
9576 \r
9577   /*\r
9578    * The steps for redirecting child's STDIN:\r
9579    *     1. Create anonymous pipe to be STDIN for child.\r
9580    *     2. Create a noninheritable duplicate of write handle,\r
9581    *         and close the inheritable write handle.\r
9582    */\r
9583 \r
9584   /* Create a pipe for the child's STDIN. */\r
9585   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9586     return GetLastError();\r
9587   }\r
9588 \r
9589   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9590   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9591                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9592                              FALSE,     /* not inherited */\r
9593                              DUPLICATE_SAME_ACCESS);\r
9594   if (! fSuccess) {\r
9595     return GetLastError();\r
9596   }\r
9597   CloseHandle(hChildStdinWr);\r
9598 \r
9599   /* Arrange to (1) look in dir for the child .exe file, and\r
9600    * (2) have dir be the child's working directory.  Interpret\r
9601    * dir relative to the directory WinBoard loaded from. */\r
9602   GetCurrentDirectory(MSG_SIZ, buf);\r
9603   SetCurrentDirectory(installDir);\r
9604   SetCurrentDirectory(dir);\r
9605 \r
9606   /* Now create the child process. */\r
9607 \r
9608   siStartInfo.cb = sizeof(STARTUPINFO);\r
9609   siStartInfo.lpReserved = NULL;\r
9610   siStartInfo.lpDesktop = NULL;\r
9611   siStartInfo.lpTitle = NULL;\r
9612   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9613   siStartInfo.cbReserved2 = 0;\r
9614   siStartInfo.lpReserved2 = NULL;\r
9615   siStartInfo.hStdInput = hChildStdinRd;\r
9616   siStartInfo.hStdOutput = hChildStdoutWr;\r
9617   siStartInfo.hStdError = hChildStdoutWr;\r
9618 \r
9619   fSuccess = CreateProcess(NULL,\r
9620                            cmdLine,        /* command line */\r
9621                            NULL,           /* process security attributes */\r
9622                            NULL,           /* primary thread security attrs */\r
9623                            TRUE,           /* handles are inherited */\r
9624                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9625                            NULL,           /* use parent's environment */\r
9626                            NULL,\r
9627                            &siStartInfo, /* STARTUPINFO pointer */\r
9628                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9629 \r
9630   err = GetLastError();\r
9631   SetCurrentDirectory(buf); /* return to prev directory */\r
9632   if (! fSuccess) {\r
9633     return err;\r
9634   }\r
9635 \r
9636   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9637     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9638     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9639   }\r
9640 \r
9641   /* Close the handles we don't need in the parent */\r
9642   CloseHandle(piProcInfo.hThread);\r
9643   CloseHandle(hChildStdinRd);\r
9644   CloseHandle(hChildStdoutWr);\r
9645 \r
9646   /* Prepare return value */\r
9647   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9648   cp->kind = CPReal;\r
9649   cp->hProcess = piProcInfo.hProcess;\r
9650   cp->pid = piProcInfo.dwProcessId;\r
9651   cp->hFrom = hChildStdoutRdDup;\r
9652   cp->hTo = hChildStdinWrDup;\r
9653 \r
9654   *pr = (void *) cp;\r
9655 \r
9656   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9657      2000 where engines sometimes don't see the initial command(s)\r
9658      from WinBoard and hang.  I don't understand how that can happen,\r
9659      but the Sleep is harmless, so I've put it in.  Others have also\r
9660      reported what may be the same problem, so hopefully this will fix\r
9661      it for them too.  */\r
9662   Sleep(500);\r
9663 \r
9664   return NO_ERROR;\r
9665 }\r
9666 \r
9667 \r
9668 void\r
9669 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9670 {\r
9671   ChildProc *cp; int result;\r
9672 \r
9673   cp = (ChildProc *) pr;\r
9674   if (cp == NULL) return;\r
9675 \r
9676   switch (cp->kind) {\r
9677   case CPReal:\r
9678     /* TerminateProcess is considered harmful, so... */\r
9679     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9680     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9681     /* The following doesn't work because the chess program\r
9682        doesn't "have the same console" as WinBoard.  Maybe\r
9683        we could arrange for this even though neither WinBoard\r
9684        nor the chess program uses a console for stdio? */\r
9685     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9686 \r
9687     /* [AS] Special termination modes for misbehaving programs... */\r
9688     if( signal == 9 ) { \r
9689         result = TerminateProcess( cp->hProcess, 0 );\r
9690 \r
9691         if ( appData.debugMode) {\r
9692             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9693         }\r
9694     }\r
9695     else if( signal == 10 ) {\r
9696         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9697 \r
9698         if( dw != WAIT_OBJECT_0 ) {\r
9699             result = TerminateProcess( cp->hProcess, 0 );\r
9700 \r
9701             if ( appData.debugMode) {\r
9702                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9703             }\r
9704 \r
9705         }\r
9706     }\r
9707 \r
9708     CloseHandle(cp->hProcess);\r
9709     break;\r
9710 \r
9711   case CPComm:\r
9712     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9713     break;\r
9714 \r
9715   case CPSock:\r
9716     closesocket(cp->sock);\r
9717     WSACleanup();\r
9718     break;\r
9719 \r
9720   case CPRcmd:\r
9721     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9722     closesocket(cp->sock);\r
9723     closesocket(cp->sock2);\r
9724     WSACleanup();\r
9725     break;\r
9726   }\r
9727   free(cp);\r
9728 }\r
9729 \r
9730 void\r
9731 InterruptChildProcess(ProcRef pr)\r
9732 {\r
9733   ChildProc *cp;\r
9734 \r
9735   cp = (ChildProc *) pr;\r
9736   if (cp == NULL) return;\r
9737   switch (cp->kind) {\r
9738   case CPReal:\r
9739     /* The following doesn't work because the chess program\r
9740        doesn't "have the same console" as WinBoard.  Maybe\r
9741        we could arrange for this even though neither WinBoard\r
9742        nor the chess program uses a console for stdio */\r
9743     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9744     break;\r
9745 \r
9746   case CPComm:\r
9747   case CPSock:\r
9748     /* Can't interrupt */\r
9749     break;\r
9750 \r
9751   case CPRcmd:\r
9752     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9753     break;\r
9754   }\r
9755 }\r
9756 \r
9757 \r
9758 int\r
9759 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9760 {\r
9761   char cmdLine[MSG_SIZ];\r
9762 \r
9763   if (port[0] == NULLCHAR) {\r
9764     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9765   } else {\r
9766     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9767   }\r
9768   return StartChildProcess(cmdLine, "", pr);\r
9769 }\r
9770 \r
9771 \r
9772 /* Code to open TCP sockets */\r
9773 \r
9774 int\r
9775 OpenTCP(char *host, char *port, ProcRef *pr)\r
9776 {\r
9777   ChildProc *cp;\r
9778   int err;\r
9779   SOCKET s;\r
9780   struct sockaddr_in sa, mysa;\r
9781   struct hostent FAR *hp;\r
9782   unsigned short uport;\r
9783   WORD wVersionRequested;\r
9784   WSADATA wsaData;\r
9785 \r
9786   /* Initialize socket DLL */\r
9787   wVersionRequested = MAKEWORD(1, 1);\r
9788   err = WSAStartup(wVersionRequested, &wsaData);\r
9789   if (err != 0) return err;\r
9790 \r
9791   /* Make socket */\r
9792   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9793     err = WSAGetLastError();\r
9794     WSACleanup();\r
9795     return err;\r
9796   }\r
9797 \r
9798   /* Bind local address using (mostly) don't-care values.\r
9799    */\r
9800   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9801   mysa.sin_family = AF_INET;\r
9802   mysa.sin_addr.s_addr = INADDR_ANY;\r
9803   uport = (unsigned short) 0;\r
9804   mysa.sin_port = htons(uport);\r
9805   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9806       == SOCKET_ERROR) {\r
9807     err = WSAGetLastError();\r
9808     WSACleanup();\r
9809     return err;\r
9810   }\r
9811 \r
9812   /* Resolve remote host name */\r
9813   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9814   if (!(hp = gethostbyname(host))) {\r
9815     unsigned int b0, b1, b2, b3;\r
9816 \r
9817     err = WSAGetLastError();\r
9818 \r
9819     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9820       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9821       hp->h_addrtype = AF_INET;\r
9822       hp->h_length = 4;\r
9823       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9824       hp->h_addr_list[0] = (char *) malloc(4);\r
9825       hp->h_addr_list[0][0] = (char) b0;\r
9826       hp->h_addr_list[0][1] = (char) b1;\r
9827       hp->h_addr_list[0][2] = (char) b2;\r
9828       hp->h_addr_list[0][3] = (char) b3;\r
9829     } else {\r
9830       WSACleanup();\r
9831       return err;\r
9832     }\r
9833   }\r
9834   sa.sin_family = hp->h_addrtype;\r
9835   uport = (unsigned short) atoi(port);\r
9836   sa.sin_port = htons(uport);\r
9837   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9838 \r
9839   /* Make connection */\r
9840   if (connect(s, (struct sockaddr *) &sa,\r
9841               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9842     err = WSAGetLastError();\r
9843     WSACleanup();\r
9844     return err;\r
9845   }\r
9846 \r
9847   /* Prepare return value */\r
9848   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9849   cp->kind = CPSock;\r
9850   cp->sock = s;\r
9851   *pr = (ProcRef *) cp;\r
9852 \r
9853   return NO_ERROR;\r
9854 }\r
9855 \r
9856 int\r
9857 OpenCommPort(char *name, ProcRef *pr)\r
9858 {\r
9859   HANDLE h;\r
9860   COMMTIMEOUTS ct;\r
9861   ChildProc *cp;\r
9862   char fullname[MSG_SIZ];\r
9863 \r
9864   if (*name != '\\')\r
9865     sprintf(fullname, "\\\\.\\%s", name);\r
9866   else\r
9867     strcpy(fullname, name);\r
9868 \r
9869   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9870                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9871   if (h == (HANDLE) -1) {\r
9872     return GetLastError();\r
9873   }\r
9874   hCommPort = h;\r
9875 \r
9876   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9877 \r
9878   /* Accumulate characters until a 100ms pause, then parse */\r
9879   ct.ReadIntervalTimeout = 100;\r
9880   ct.ReadTotalTimeoutMultiplier = 0;\r
9881   ct.ReadTotalTimeoutConstant = 0;\r
9882   ct.WriteTotalTimeoutMultiplier = 0;\r
9883   ct.WriteTotalTimeoutConstant = 0;\r
9884   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9885 \r
9886   /* Prepare return value */\r
9887   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9888   cp->kind = CPComm;\r
9889   cp->hFrom = h;\r
9890   cp->hTo = h;\r
9891   *pr = (ProcRef *) cp;\r
9892 \r
9893   return NO_ERROR;\r
9894 }\r
9895 \r
9896 int\r
9897 OpenLoopback(ProcRef *pr)\r
9898 {\r
9899   DisplayFatalError("Not implemented", 0, 1);\r
9900   return NO_ERROR;\r
9901 }\r
9902 \r
9903 \r
9904 int\r
9905 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9906 {\r
9907   ChildProc *cp;\r
9908   int err;\r
9909   SOCKET s, s2, s3;\r
9910   struct sockaddr_in sa, mysa;\r
9911   struct hostent FAR *hp;\r
9912   unsigned short uport;\r
9913   WORD wVersionRequested;\r
9914   WSADATA wsaData;\r
9915   int fromPort;\r
9916   char stderrPortStr[MSG_SIZ];\r
9917 \r
9918   /* Initialize socket DLL */\r
9919   wVersionRequested = MAKEWORD(1, 1);\r
9920   err = WSAStartup(wVersionRequested, &wsaData);\r
9921   if (err != 0) return err;\r
9922 \r
9923   /* Resolve remote host name */\r
9924   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9925   if (!(hp = gethostbyname(host))) {\r
9926     unsigned int b0, b1, b2, b3;\r
9927 \r
9928     err = WSAGetLastError();\r
9929 \r
9930     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9931       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9932       hp->h_addrtype = AF_INET;\r
9933       hp->h_length = 4;\r
9934       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9935       hp->h_addr_list[0] = (char *) malloc(4);\r
9936       hp->h_addr_list[0][0] = (char) b0;\r
9937       hp->h_addr_list[0][1] = (char) b1;\r
9938       hp->h_addr_list[0][2] = (char) b2;\r
9939       hp->h_addr_list[0][3] = (char) b3;\r
9940     } else {\r
9941       WSACleanup();\r
9942       return err;\r
9943     }\r
9944   }\r
9945   sa.sin_family = hp->h_addrtype;\r
9946   uport = (unsigned short) 514;\r
9947   sa.sin_port = htons(uport);\r
9948   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9949 \r
9950   /* Bind local socket to unused "privileged" port address\r
9951    */\r
9952   s = INVALID_SOCKET;\r
9953   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9954   mysa.sin_family = AF_INET;\r
9955   mysa.sin_addr.s_addr = INADDR_ANY;\r
9956   for (fromPort = 1023;; fromPort--) {\r
9957     if (fromPort < 0) {\r
9958       WSACleanup();\r
9959       return WSAEADDRINUSE;\r
9960     }\r
9961     if (s == INVALID_SOCKET) {\r
9962       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9963         err = WSAGetLastError();\r
9964         WSACleanup();\r
9965         return err;\r
9966       }\r
9967     }\r
9968     uport = (unsigned short) fromPort;\r
9969     mysa.sin_port = htons(uport);\r
9970     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9971         == SOCKET_ERROR) {\r
9972       err = WSAGetLastError();\r
9973       if (err == WSAEADDRINUSE) continue;\r
9974       WSACleanup();\r
9975       return err;\r
9976     }\r
9977     if (connect(s, (struct sockaddr *) &sa,\r
9978       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9979       err = WSAGetLastError();\r
9980       if (err == WSAEADDRINUSE) {\r
9981         closesocket(s);\r
9982         s = -1;\r
9983         continue;\r
9984       }\r
9985       WSACleanup();\r
9986       return err;\r
9987     }\r
9988     break;\r
9989   }\r
9990 \r
9991   /* Bind stderr local socket to unused "privileged" port address\r
9992    */\r
9993   s2 = INVALID_SOCKET;\r
9994   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9995   mysa.sin_family = AF_INET;\r
9996   mysa.sin_addr.s_addr = INADDR_ANY;\r
9997   for (fromPort = 1023;; fromPort--) {\r
9998     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9999     if (fromPort < 0) {\r
10000       (void) closesocket(s);\r
10001       WSACleanup();\r
10002       return WSAEADDRINUSE;\r
10003     }\r
10004     if (s2 == INVALID_SOCKET) {\r
10005       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10006         err = WSAGetLastError();\r
10007         closesocket(s);\r
10008         WSACleanup();\r
10009         return err;\r
10010       }\r
10011     }\r
10012     uport = (unsigned short) fromPort;\r
10013     mysa.sin_port = htons(uport);\r
10014     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10015         == SOCKET_ERROR) {\r
10016       err = WSAGetLastError();\r
10017       if (err == WSAEADDRINUSE) continue;\r
10018       (void) closesocket(s);\r
10019       WSACleanup();\r
10020       return err;\r
10021     }\r
10022     if (listen(s2, 1) == SOCKET_ERROR) {\r
10023       err = WSAGetLastError();\r
10024       if (err == WSAEADDRINUSE) {\r
10025         closesocket(s2);\r
10026         s2 = INVALID_SOCKET;\r
10027         continue;\r
10028       }\r
10029       (void) closesocket(s);\r
10030       (void) closesocket(s2);\r
10031       WSACleanup();\r
10032       return err;\r
10033     }\r
10034     break;\r
10035   }\r
10036   prevStderrPort = fromPort; // remember port used\r
10037   sprintf(stderrPortStr, "%d", fromPort);\r
10038 \r
10039   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10040     err = WSAGetLastError();\r
10041     (void) closesocket(s);\r
10042     (void) closesocket(s2);\r
10043     WSACleanup();\r
10044     return err;\r
10045   }\r
10046 \r
10047   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10048     err = WSAGetLastError();\r
10049     (void) closesocket(s);\r
10050     (void) closesocket(s2);\r
10051     WSACleanup();\r
10052     return err;\r
10053   }\r
10054   if (*user == NULLCHAR) user = UserName();\r
10055   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10056     err = WSAGetLastError();\r
10057     (void) closesocket(s);\r
10058     (void) closesocket(s2);\r
10059     WSACleanup();\r
10060     return err;\r
10061   }\r
10062   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10063     err = WSAGetLastError();\r
10064     (void) closesocket(s);\r
10065     (void) closesocket(s2);\r
10066     WSACleanup();\r
10067     return err;\r
10068   }\r
10069 \r
10070   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10071     err = WSAGetLastError();\r
10072     (void) closesocket(s);\r
10073     (void) closesocket(s2);\r
10074     WSACleanup();\r
10075     return err;\r
10076   }\r
10077   (void) closesocket(s2);  /* Stop listening */\r
10078 \r
10079   /* Prepare return value */\r
10080   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10081   cp->kind = CPRcmd;\r
10082   cp->sock = s;\r
10083   cp->sock2 = s3;\r
10084   *pr = (ProcRef *) cp;\r
10085 \r
10086   return NO_ERROR;\r
10087 }\r
10088 \r
10089 \r
10090 InputSourceRef\r
10091 AddInputSource(ProcRef pr, int lineByLine,\r
10092                InputCallback func, VOIDSTAR closure)\r
10093 {\r
10094   InputSource *is, *is2 = NULL;\r
10095   ChildProc *cp = (ChildProc *) pr;\r
10096 \r
10097   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10098   is->lineByLine = lineByLine;\r
10099   is->func = func;\r
10100   is->closure = closure;\r
10101   is->second = NULL;\r
10102   is->next = is->buf;\r
10103   if (pr == NoProc) {\r
10104     is->kind = CPReal;\r
10105     consoleInputSource = is;\r
10106   } else {\r
10107     is->kind = cp->kind;\r
10108     /* \r
10109         [AS] Try to avoid a race condition if the thread is given control too early:\r
10110         we create all threads suspended so that the is->hThread variable can be\r
10111         safely assigned, then let the threads start with ResumeThread.\r
10112     */\r
10113     switch (cp->kind) {\r
10114     case CPReal:\r
10115       is->hFile = cp->hFrom;\r
10116       cp->hFrom = NULL; /* now owned by InputThread */\r
10117       is->hThread =\r
10118         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10119                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10120       break;\r
10121 \r
10122     case CPComm:\r
10123       is->hFile = cp->hFrom;\r
10124       cp->hFrom = NULL; /* now owned by InputThread */\r
10125       is->hThread =\r
10126         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10127                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10128       break;\r
10129 \r
10130     case CPSock:\r
10131       is->sock = cp->sock;\r
10132       is->hThread =\r
10133         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10134                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10135       break;\r
10136 \r
10137     case CPRcmd:\r
10138       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10139       *is2 = *is;\r
10140       is->sock = cp->sock;\r
10141       is->second = is2;\r
10142       is2->sock = cp->sock2;\r
10143       is2->second = is2;\r
10144       is->hThread =\r
10145         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10146                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10147       is2->hThread =\r
10148         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10149                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10150       break;\r
10151     }\r
10152 \r
10153     if( is->hThread != NULL ) {\r
10154         ResumeThread( is->hThread );\r
10155     }\r
10156 \r
10157     if( is2 != NULL && is2->hThread != NULL ) {\r
10158         ResumeThread( is2->hThread );\r
10159     }\r
10160   }\r
10161 \r
10162   return (InputSourceRef) is;\r
10163 }\r
10164 \r
10165 void\r
10166 RemoveInputSource(InputSourceRef isr)\r
10167 {\r
10168   InputSource *is;\r
10169 \r
10170   is = (InputSource *) isr;\r
10171   is->hThread = NULL;  /* tell thread to stop */\r
10172   CloseHandle(is->hThread);\r
10173   if (is->second != NULL) {\r
10174     is->second->hThread = NULL;\r
10175     CloseHandle(is->second->hThread);\r
10176   }\r
10177 }\r
10178 \r
10179 \r
10180 int\r
10181 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10182 {\r
10183   DWORD dOutCount;\r
10184   int outCount = SOCKET_ERROR;\r
10185   ChildProc *cp = (ChildProc *) pr;\r
10186   static OVERLAPPED ovl;\r
10187 \r
10188   if (pr == NoProc) {\r
10189     ConsoleOutput(message, count, FALSE);\r
10190     return count;\r
10191   } \r
10192 \r
10193   if (ovl.hEvent == NULL) {\r
10194     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10195   }\r
10196   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10197 \r
10198   switch (cp->kind) {\r
10199   case CPSock:\r
10200   case CPRcmd:\r
10201     outCount = send(cp->sock, message, count, 0);\r
10202     if (outCount == SOCKET_ERROR) {\r
10203       *outError = WSAGetLastError();\r
10204     } else {\r
10205       *outError = NO_ERROR;\r
10206     }\r
10207     break;\r
10208 \r
10209   case CPReal:\r
10210     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10211                   &dOutCount, NULL)) {\r
10212       *outError = NO_ERROR;\r
10213       outCount = (int) dOutCount;\r
10214     } else {\r
10215       *outError = GetLastError();\r
10216     }\r
10217     break;\r
10218 \r
10219   case CPComm:\r
10220     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10221                             &dOutCount, &ovl);\r
10222     if (*outError == NO_ERROR) {\r
10223       outCount = (int) dOutCount;\r
10224     }\r
10225     break;\r
10226   }\r
10227   return outCount;\r
10228 }\r
10229 \r
10230 int\r
10231 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10232                        long msdelay)\r
10233 {\r
10234   /* Ignore delay, not implemented for WinBoard */\r
10235   return OutputToProcess(pr, message, count, outError);\r
10236 }\r
10237 \r
10238 \r
10239 void\r
10240 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10241                         char *buf, int count, int error)\r
10242 {\r
10243   DisplayFatalError("Not implemented", 0, 1);\r
10244 }\r
10245 \r
10246 /* see wgamelist.c for Game List functions */\r
10247 /* see wedittags.c for Edit Tags functions */\r
10248 \r
10249 \r
10250 VOID\r
10251 ICSInitScript()\r
10252 {\r
10253   FILE *f;\r
10254   char buf[MSG_SIZ];\r
10255   char *dummy;\r
10256 \r
10257   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10258     f = fopen(buf, "r");\r
10259     if (f != NULL) {\r
10260       ProcessICSInitScript(f);\r
10261       fclose(f);\r
10262     }\r
10263   }\r
10264 }\r
10265 \r
10266 \r
10267 VOID\r
10268 StartAnalysisClock()\r
10269 {\r
10270   if (analysisTimerEvent) return;\r
10271   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10272                                         (UINT) 2000, NULL);\r
10273 }\r
10274 \r
10275 LRESULT CALLBACK\r
10276 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10277 {\r
10278   static HANDLE hwndText;\r
10279   RECT rect;\r
10280   static int sizeX, sizeY;\r
10281   int newSizeX, newSizeY, flags;\r
10282   MINMAXINFO *mmi;\r
10283 \r
10284   switch (message) {\r
10285   case WM_INITDIALOG: /* message: initialize dialog box */\r
10286     /* Initialize the dialog items */\r
10287     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10288     SetWindowText(hDlg, analysisTitle);\r
10289     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10290     /* Size and position the dialog */\r
10291     if (!analysisDialog) {\r
10292       analysisDialog = hDlg;\r
10293       flags = SWP_NOZORDER;\r
10294       GetClientRect(hDlg, &rect);\r
10295       sizeX = rect.right;\r
10296       sizeY = rect.bottom;\r
10297       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10298           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10299         WINDOWPLACEMENT wp;\r
10300         EnsureOnScreen(&analysisX, &analysisY);\r
10301         wp.length = sizeof(WINDOWPLACEMENT);\r
10302         wp.flags = 0;\r
10303         wp.showCmd = SW_SHOW;\r
10304         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10305         wp.rcNormalPosition.left = analysisX;\r
10306         wp.rcNormalPosition.right = analysisX + analysisW;\r
10307         wp.rcNormalPosition.top = analysisY;\r
10308         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10309         SetWindowPlacement(hDlg, &wp);\r
10310 \r
10311         GetClientRect(hDlg, &rect);\r
10312         newSizeX = rect.right;\r
10313         newSizeY = rect.bottom;\r
10314         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10315                               newSizeX, newSizeY);\r
10316         sizeX = newSizeX;\r
10317         sizeY = newSizeY;\r
10318       }\r
10319     }\r
10320     return FALSE;\r
10321 \r
10322   case WM_COMMAND: /* message: received a command */\r
10323     switch (LOWORD(wParam)) {\r
10324     case IDCANCEL:\r
10325       EditGameEvent();\r
10326       return TRUE;\r
10327     default:\r
10328       break;\r
10329     }\r
10330     break;\r
10331 \r
10332   case WM_SIZE:\r
10333     newSizeX = LOWORD(lParam);\r
10334     newSizeY = HIWORD(lParam);\r
10335     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10336     sizeX = newSizeX;\r
10337     sizeY = newSizeY;\r
10338     break;\r
10339 \r
10340   case WM_GETMINMAXINFO:\r
10341     /* Prevent resizing window too small */\r
10342     mmi = (MINMAXINFO *) lParam;\r
10343     mmi->ptMinTrackSize.x = 100;\r
10344     mmi->ptMinTrackSize.y = 100;\r
10345     break;\r
10346   }\r
10347   return FALSE;\r
10348 }\r
10349 \r
10350 VOID\r
10351 AnalysisPopUp(char* title, char* str)\r
10352 {\r
10353   FARPROC lpProc;\r
10354   char *p, *q;\r
10355 \r
10356   /* [AS] */\r
10357   EngineOutputPopUp();\r
10358   return;\r
10359 \r
10360   if (str == NULL) str = "";\r
10361   p = (char *) malloc(2 * strlen(str) + 2);\r
10362   q = p;\r
10363   while (*str) {\r
10364     if (*str == '\n') *q++ = '\r';\r
10365     *q++ = *str++;\r
10366   }\r
10367   *q = NULLCHAR;\r
10368   if (analysisText != NULL) free(analysisText);\r
10369   analysisText = p;\r
10370 \r
10371   if (analysisDialog) {\r
10372     SetWindowText(analysisDialog, title);\r
10373     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10374     ShowWindow(analysisDialog, SW_SHOW);\r
10375   } else {\r
10376     analysisTitle = title;\r
10377     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10378     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10379                  hwndMain, (DLGPROC)lpProc);\r
10380     FreeProcInstance(lpProc);\r
10381   }\r
10382   analysisDialogUp = TRUE;  \r
10383 }\r
10384 \r
10385 VOID\r
10386 AnalysisPopDown()\r
10387 {\r
10388   if (analysisDialog) {\r
10389     ShowWindow(analysisDialog, SW_HIDE);\r
10390   }\r
10391   analysisDialogUp = FALSE;  \r
10392 }\r
10393 \r
10394 \r
10395 VOID\r
10396 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10397 {\r
10398   highlightInfo.sq[0].x = fromX;\r
10399   highlightInfo.sq[0].y = fromY;\r
10400   highlightInfo.sq[1].x = toX;\r
10401   highlightInfo.sq[1].y = toY;\r
10402 }\r
10403 \r
10404 VOID\r
10405 ClearHighlights()\r
10406 {\r
10407   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10408     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10409 }\r
10410 \r
10411 VOID\r
10412 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10413 {\r
10414   premoveHighlightInfo.sq[0].x = fromX;\r
10415   premoveHighlightInfo.sq[0].y = fromY;\r
10416   premoveHighlightInfo.sq[1].x = toX;\r
10417   premoveHighlightInfo.sq[1].y = toY;\r
10418 }\r
10419 \r
10420 VOID\r
10421 ClearPremoveHighlights()\r
10422 {\r
10423   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10424     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10425 }\r
10426 \r
10427 VOID\r
10428 ShutDownFrontEnd()\r
10429 {\r
10430   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10431   DeleteClipboardTempFiles();\r
10432 }\r
10433 \r
10434 void\r
10435 BoardToTop()\r
10436 {\r
10437     if (IsIconic(hwndMain))\r
10438       ShowWindow(hwndMain, SW_RESTORE);\r
10439 \r
10440     SetActiveWindow(hwndMain);\r
10441 }\r
10442 \r
10443 /*\r
10444  * Prototypes for animation support routines\r
10445  */\r
10446 static void ScreenSquare(int column, int row, POINT * pt);\r
10447 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10448      POINT frames[], int * nFrames);\r
10449 \r
10450 \r
10451 #define kFactor 4\r
10452 \r
10453 void\r
10454 AnimateMove(board, fromX, fromY, toX, toY)\r
10455      Board board;\r
10456      int fromX;\r
10457      int fromY;\r
10458      int toX;\r
10459      int toY;\r
10460 {\r
10461   ChessSquare piece;\r
10462   POINT start, finish, mid;\r
10463   POINT frames[kFactor * 2 + 1];\r
10464   int nFrames, n;\r
10465 \r
10466   if (!appData.animate) return;\r
10467   if (doingSizing) return;\r
10468   if (fromY < 0 || fromX < 0) return;\r
10469   piece = board[fromY][fromX];\r
10470   if (piece >= EmptySquare) return;\r
10471 \r
10472   ScreenSquare(fromX, fromY, &start);\r
10473   ScreenSquare(toX, toY, &finish);\r
10474 \r
10475   /* All pieces except knights move in straight line */\r
10476   if (piece != WhiteKnight && piece != BlackKnight) {\r
10477     mid.x = start.x + (finish.x - start.x) / 2;\r
10478     mid.y = start.y + (finish.y - start.y) / 2;\r
10479   } else {\r
10480     /* Knight: make diagonal movement then straight */\r
10481     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10482        mid.x = start.x + (finish.x - start.x) / 2;\r
10483        mid.y = finish.y;\r
10484      } else {\r
10485        mid.x = finish.x;\r
10486        mid.y = start.y + (finish.y - start.y) / 2;\r
10487      }\r
10488   }\r
10489   \r
10490   /* Don't use as many frames for very short moves */\r
10491   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10492     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10493   else\r
10494     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10495 \r
10496   animInfo.from.x = fromX;\r
10497   animInfo.from.y = fromY;\r
10498   animInfo.to.x = toX;\r
10499   animInfo.to.y = toY;\r
10500   animInfo.lastpos = start;\r
10501   animInfo.piece = piece;\r
10502   for (n = 0; n < nFrames; n++) {\r
10503     animInfo.pos = frames[n];\r
10504     DrawPosition(FALSE, NULL);\r
10505     animInfo.lastpos = animInfo.pos;\r
10506     Sleep(appData.animSpeed);\r
10507   }\r
10508   animInfo.pos = finish;\r
10509   DrawPosition(FALSE, NULL);\r
10510   animInfo.piece = EmptySquare;\r
10511 }\r
10512 \r
10513 /*      Convert board position to corner of screen rect and color       */\r
10514 \r
10515 static void\r
10516 ScreenSquare(column, row, pt)\r
10517      int column; int row; POINT * pt;\r
10518 {\r
10519   if (flipView) {\r
10520     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10521     pt->y = lineGap + row * (squareSize + lineGap);\r
10522   } else {\r
10523     pt->x = lineGap + column * (squareSize + lineGap);\r
10524     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10525   }\r
10526 }\r
10527 \r
10528 /*      Generate a series of frame coords from start->mid->finish.\r
10529         The movement rate doubles until the half way point is\r
10530         reached, then halves back down to the final destination,\r
10531         which gives a nice slow in/out effect. The algorithmn\r
10532         may seem to generate too many intermediates for short\r
10533         moves, but remember that the purpose is to attract the\r
10534         viewers attention to the piece about to be moved and\r
10535         then to where it ends up. Too few frames would be less\r
10536         noticeable.                                             */\r
10537 \r
10538 static void\r
10539 Tween(start, mid, finish, factor, frames, nFrames)\r
10540      POINT * start; POINT * mid;\r
10541      POINT * finish; int factor;\r
10542      POINT frames[]; int * nFrames;\r
10543 {\r
10544   int n, fraction = 1, count = 0;\r
10545 \r
10546   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10547   for (n = 0; n < factor; n++)\r
10548     fraction *= 2;\r
10549   for (n = 0; n < factor; n++) {\r
10550     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10551     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10552     count ++;\r
10553     fraction = fraction / 2;\r
10554   }\r
10555   \r
10556   /* Midpoint */\r
10557   frames[count] = *mid;\r
10558   count ++;\r
10559   \r
10560   /* Slow out, stepping 1/2, then 1/4, ... */\r
10561   fraction = 2;\r
10562   for (n = 0; n < factor; n++) {\r
10563     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10564     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10565     count ++;\r
10566     fraction = fraction * 2;\r
10567   }\r
10568   *nFrames = count;\r
10569 }\r
10570 \r
10571 void\r
10572 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10573 {\r
10574 #if 0\r
10575     char buf[256];\r
10576 \r
10577     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10578         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10579 \r
10580     OutputDebugString( buf );\r
10581 #endif\r
10582 \r
10583     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10584 \r
10585     EvalGraphSet( first, last, current, pvInfoList );\r
10586 }\r
10587 \r
10588 void SetProgramStats( FrontEndProgramStats * stats )\r
10589 {\r
10590 #if 0\r
10591     char buf[1024];\r
10592 \r
10593     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10594         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10595 \r
10596     OutputDebugString( buf );\r
10597 #endif\r
10598 \r
10599     EngineOutputUpdate( stats );\r
10600 }\r