dbf3c900db0750f772456d45f57be85a0a057e09
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect;\r
144 static char messageText[MESSAGE_TEXT_MAX];\r
145 static int clockTimerEvent = 0;\r
146 static int loadGameTimerEvent = 0;\r
147 static int analysisTimerEvent = 0;\r
148 static DelayedEventCallback delayedTimerCallback;\r
149 static int delayedTimerEvent = 0;\r
150 static int buttonCount = 2;\r
151 char *icsTextMenuString;\r
152 char *icsNames;\r
153 char *firstChessProgramNames;\r
154 char *secondChessProgramNames;\r
155 \r
156 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
157 \r
158 #define PALETTESIZE 256\r
159 \r
160 HINSTANCE hInst;          /* current instance */\r
161 HWND hwndMain = NULL;        /* root window*/\r
162 HWND hwndConsole = NULL;\r
163 BOOLEAN alwaysOnTop = FALSE;\r
164 RECT boardRect;\r
165 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
166   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
167 HPALETTE hPal;\r
168 ColorClass currentColorClass;\r
169 \r
170 HWND hCommPort = NULL;    /* currently open comm port */\r
171 static HWND hwndPause;    /* pause button */\r
172 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
173 static HBRUSH lightSquareBrush, darkSquareBrush,\r
174   blackSquareBrush, /* [HGM] for band between board and holdings */\r
175   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
176 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
177 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
178 static HPEN gridPen = NULL;\r
179 static HPEN highlightPen = NULL;\r
180 static HPEN premovePen = NULL;\r
181 static NPLOGPALETTE pLogPal;\r
182 static BOOL paletteChanged = FALSE;\r
183 static HICON iconWhite, iconBlack, iconCurrent;\r
184 static int doingSizing = FALSE;\r
185 static int lastSizing = 0;\r
186 static int prevStderrPort;\r
187 \r
188 /* [AS] Support for background textures */\r
189 #define BACK_TEXTURE_MODE_DISABLED      0\r
190 #define BACK_TEXTURE_MODE_PLAIN         1\r
191 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
192 \r
193 static HBITMAP liteBackTexture = NULL;\r
194 static HBITMAP darkBackTexture = NULL;\r
195 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
196 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int backTextureSquareSize = 0;\r
198 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
199 \r
200 #if __GNUC__ && !defined(_winmajor)\r
201 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
202 #else\r
203 #define oldDialog (_winmajor < 4)\r
204 #endif\r
205 \r
206 char *defaultTextAttribs[] = \r
207 {\r
208   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
209   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
210   COLOR_NONE\r
211 };\r
212 \r
213 typedef struct {\r
214   char *name;\r
215   int squareSize;\r
216   int lineGap;\r
217   int smallLayout;\r
218   int tinyLayout;\r
219   int cliWidth, cliHeight;\r
220 } SizeInfo;\r
221 \r
222 SizeInfo sizeInfo[] = \r
223 {\r
224   { "tiny",     21, 0, 1, 1, 0, 0 },\r
225   { "teeny",    25, 1, 1, 1, 0, 0 },\r
226   { "dinky",    29, 1, 1, 1, 0, 0 },\r
227   { "petite",   33, 1, 1, 1, 0, 0 },\r
228   { "slim",     37, 2, 1, 0, 0, 0 },\r
229   { "small",    40, 2, 1, 0, 0, 0 },\r
230   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
231   { "middling", 49, 2, 0, 0, 0, 0 },\r
232   { "average",  54, 2, 0, 0, 0, 0 },\r
233   { "moderate", 58, 3, 0, 0, 0, 0 },\r
234   { "medium",   64, 3, 0, 0, 0, 0 },\r
235   { "bulky",    72, 3, 0, 0, 0, 0 },\r
236   { "large",    80, 3, 0, 0, 0, 0 },\r
237   { "big",      87, 3, 0, 0, 0, 0 },\r
238   { "huge",     95, 3, 0, 0, 0, 0 },\r
239   { "giant",    108, 3, 0, 0, 0, 0 },\r
240   { "colossal", 116, 4, 0, 0, 0, 0 },\r
241   { "titanic",  129, 4, 0, 0, 0, 0 },\r
242   { NULL, 0, 0, 0, 0, 0, 0 }\r
243 };\r
244 \r
245 #define MF(x) {x, {0, }, {0, }, 0}\r
246 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
247 {\r
248   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
249   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
250   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
251   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
252   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
253   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
254   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
255   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
256   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
257   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
258   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
259   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
260   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
261   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
262   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
263   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
264   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
265   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
266 };\r
267 \r
268 MyFont *font[NUM_SIZES][NUM_FONTS];\r
269 \r
270 typedef struct {\r
271   char *label;\r
272   int id;\r
273   HWND hwnd;\r
274   WNDPROC wndproc;\r
275 } MyButtonDesc;\r
276 \r
277 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
278 #define N_BUTTONS 5\r
279 \r
280 MyButtonDesc buttonDesc[N_BUTTONS] =\r
281 {\r
282   {"<<", IDM_ToStart, NULL, NULL},\r
283   {"<", IDM_Backward, NULL, NULL},\r
284   {"P", IDM_Pause, NULL, NULL},\r
285   {">", IDM_Forward, NULL, NULL},\r
286   {">>", IDM_ToEnd, NULL, NULL},\r
287 };\r
288 \r
289 int tinyLayout = 0, smallLayout = 0;\r
290 #define MENU_BAR_ITEMS 6\r
291 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
292   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
293   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
294 };\r
295 \r
296 \r
297 MySound sounds[(int)NSoundClasses];\r
298 MyTextAttribs textAttribs[(int)NColorClasses];\r
299 \r
300 MyColorizeAttribs colorizeAttribs[] = {\r
301   { (COLORREF)0, 0, "Shout Text" },\r
302   { (COLORREF)0, 0, "SShout/CShout" },\r
303   { (COLORREF)0, 0, "Channel 1 Text" },\r
304   { (COLORREF)0, 0, "Channel Text" },\r
305   { (COLORREF)0, 0, "Kibitz Text" },\r
306   { (COLORREF)0, 0, "Tell Text" },\r
307   { (COLORREF)0, 0, "Challenge Text" },\r
308   { (COLORREF)0, 0, "Request Text" },\r
309   { (COLORREF)0, 0, "Seek Text" },\r
310   { (COLORREF)0, 0, "Normal Text" },\r
311   { (COLORREF)0, 0, "None" }\r
312 };\r
313 \r
314 \r
315 \r
316 static char *commentTitle;\r
317 static char *commentText;\r
318 static int commentIndex;\r
319 static Boolean editComment = FALSE;\r
320 HWND commentDialog = NULL;\r
321 BOOLEAN commentDialogUp = FALSE;\r
322 static int commentX, commentY, commentH, commentW;\r
323 \r
324 static char *analysisTitle;\r
325 static char *analysisText;\r
326 HWND analysisDialog = NULL;\r
327 BOOLEAN analysisDialogUp = FALSE;\r
328 static int analysisX, analysisY, analysisH, analysisW;\r
329 \r
330 char errorTitle[MSG_SIZ];\r
331 char errorMessage[2*MSG_SIZ];\r
332 HWND errorDialog = NULL;\r
333 BOOLEAN moveErrorMessageUp = FALSE;\r
334 BOOLEAN consoleEcho = TRUE;\r
335 CHARFORMAT consoleCF;\r
336 COLORREF consoleBackgroundColor;\r
337 \r
338 char *programVersion;\r
339 \r
340 #define CPReal 1\r
341 #define CPComm 2\r
342 #define CPSock 3\r
343 #define CPRcmd 4\r
344 typedef int CPKind;\r
345 \r
346 typedef struct {\r
347   CPKind kind;\r
348   HANDLE hProcess;\r
349   DWORD pid;\r
350   HANDLE hTo;\r
351   HANDLE hFrom;\r
352   SOCKET sock;\r
353   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
354 } ChildProc;\r
355 \r
356 #define INPUT_SOURCE_BUF_SIZE 4096\r
357 \r
358 typedef struct _InputSource {\r
359   CPKind kind;\r
360   HANDLE hFile;\r
361   SOCKET sock;\r
362   int lineByLine;\r
363   HANDLE hThread;\r
364   DWORD id;\r
365   char buf[INPUT_SOURCE_BUF_SIZE];\r
366   char *next;\r
367   DWORD count;\r
368   int error;\r
369   InputCallback func;\r
370   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
371   VOIDSTAR closure;\r
372 } InputSource;\r
373 \r
374 InputSource *consoleInputSource;\r
375 \r
376 DCB dcb;\r
377 \r
378 /* forward */\r
379 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
380 VOID ConsoleCreate();\r
381 LRESULT CALLBACK\r
382   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
383 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
384 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
385 VOID ParseCommSettings(char *arg, DCB *dcb);\r
386 LRESULT CALLBACK\r
387   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
389 void ParseIcsTextMenu(char *icsTextMenuString);\r
390 VOID PopUpMoveDialog(char firstchar);\r
391 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
392 \r
393 /* [AS] */\r
394 int NewGameFRC();\r
395 int GameListOptions();\r
396 \r
397 HWND moveHistoryDialog = NULL;\r
398 BOOLEAN moveHistoryDialogUp = FALSE;\r
399 \r
400 WindowPlacement wpMoveHistory;\r
401 \r
402 HWND evalGraphDialog = NULL;\r
403 BOOLEAN evalGraphDialogUp = FALSE;\r
404 \r
405 WindowPlacement wpEvalGraph;\r
406 \r
407 HWND engineOutputDialog = NULL;\r
408 BOOLEAN engineOutputDialogUp = FALSE;\r
409 \r
410 WindowPlacement wpEngineOutput;\r
411 \r
412 VOID MoveHistoryPopUp();\r
413 VOID MoveHistoryPopDown();\r
414 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
415 BOOL MoveHistoryIsUp();\r
416 \r
417 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
418 VOID EvalGraphPopUp();\r
419 VOID EvalGraphPopDown();\r
420 BOOL EvalGraphIsUp();\r
421 \r
422 VOID EngineOutputPopUp();\r
423 VOID EngineOutputPopDown();\r
424 BOOL EngineOutputIsUp();\r
425 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
426 \r
427 VOID GothicPopUp(char *title, VariantClass variant);\r
428 /*\r
429  * Setting "frozen" should disable all user input other than deleting\r
430  * the window.  We do this while engines are initializing themselves.\r
431  */\r
432 static int frozen = 0;\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];\r
434 void FreezeUI()\r
435 {\r
436   HMENU hmenu;\r
437   int i;\r
438 \r
439   if (frozen) return;\r
440   frozen = 1;\r
441   hmenu = GetMenu(hwndMain);\r
442   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
443     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
444   }\r
445   DrawMenuBar(hwndMain);\r
446 }\r
447 \r
448 /* Undo a FreezeUI */\r
449 void ThawUI()\r
450 {\r
451   HMENU hmenu;\r
452   int i;\r
453 \r
454   if (!frozen) return;\r
455   frozen = 0;\r
456   hmenu = GetMenu(hwndMain);\r
457   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
458     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
459   }\r
460   DrawMenuBar(hwndMain);\r
461 }\r
462 \r
463 /*---------------------------------------------------------------------------*\\r
464  *\r
465  * WinMain\r
466  *\r
467 \*---------------------------------------------------------------------------*/\r
468 \r
469 int APIENTRY\r
470 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
471         LPSTR lpCmdLine, int nCmdShow)\r
472 {\r
473   MSG msg;\r
474   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
475 \r
476   debugFP = stderr;\r
477 \r
478   LoadLibrary("RICHED32.DLL");\r
479   consoleCF.cbSize = sizeof(CHARFORMAT);\r
480 \r
481   if (!InitApplication(hInstance)) {\r
482     return (FALSE);\r
483   }\r
484   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
485     return (FALSE);\r
486   }\r
487 \r
488   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
489   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
490   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
491 \r
492   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
493 \r
494   while (GetMessage(&msg, /* message structure */\r
495                     NULL, /* handle of window receiving the message */\r
496                     0,    /* lowest message to examine */\r
497                     0))   /* highest message to examine */\r
498     {\r
499       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
500           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
501           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
502           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
503           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
504           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
505           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
506           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
507           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
508           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
509         TranslateMessage(&msg); /* Translates virtual key codes */\r
510         DispatchMessage(&msg);  /* Dispatches message to window */\r
511       }\r
512     }\r
513 \r
514 \r
515   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
516 }\r
517 \r
518 /*---------------------------------------------------------------------------*\\r
519  *\r
520  * Initialization functions\r
521  *\r
522 \*---------------------------------------------------------------------------*/\r
523 \r
524 BOOL\r
525 InitApplication(HINSTANCE hInstance)\r
526 {\r
527   WNDCLASS wc;\r
528 \r
529   /* Fill in window class structure with parameters that describe the */\r
530   /* main window. */\r
531 \r
532   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
533   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
534   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
535   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
536   wc.hInstance     = hInstance;         /* Owner of this class */\r
537   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
538   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
539   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
540   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
541   wc.lpszClassName = szAppName;                 /* Name to register as */\r
542 \r
543   /* Register the window class and return success/failure code. */\r
544   if (!RegisterClass(&wc)) return FALSE;\r
545 \r
546   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
547   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
548   wc.cbClsExtra    = 0;\r
549   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
550   wc.hInstance     = hInstance;\r
551   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
552   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
553   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
554   wc.lpszMenuName  = NULL;\r
555   wc.lpszClassName = szConsoleName;\r
556 \r
557   if (!RegisterClass(&wc)) return FALSE;\r
558   return TRUE;\r
559 }\r
560 \r
561 \r
562 /* Set by InitInstance, used by EnsureOnScreen */\r
563 int screenHeight, screenWidth;\r
564 \r
565 void\r
566 EnsureOnScreen(int *x, int *y)\r
567 {\r
568   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
569   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
570   if (*x > screenWidth - 32) *x = 0;\r
571   if (*y > screenHeight - 32) *y = 0;\r
572   if (*x < 10) *x = 10;\r
573   if (*y < gap) *y = gap;\r
574 }\r
575 \r
576 BOOL\r
577 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
578 {\r
579   HWND hwnd; /* Main window handle. */\r
580   int ibs;\r
581   WINDOWPLACEMENT wp;\r
582   char *filepart;\r
583 \r
584   hInst = hInstance;    /* Store instance handle in our global variable */\r
585 \r
586   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
587     *filepart = NULLCHAR;\r
588   } else {\r
589     GetCurrentDirectory(MSG_SIZ, installDir);\r
590   }\r
591   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
592   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
593   if (appData.debugMode) {\r
594     debugFP = fopen(appData.nameOfDebugFile, "w");\r
595     setbuf(debugFP, NULL);\r
596   }\r
597 \r
598   InitBackEnd1();\r
599 \r
600   InitEngineUCI( installDir, &first );\r
601   InitEngineUCI( installDir, &second );\r
602 \r
603   /* Create a main window for this application instance. */\r
604   hwnd = CreateWindow(szAppName, szTitle,\r
605                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
606                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
607                       NULL, NULL, hInstance, NULL);\r
608   hwndMain = hwnd;\r
609 \r
610   /* If window could not be created, return "failure" */\r
611   if (!hwnd) {\r
612     return (FALSE);\r
613   }\r
614 \r
615   iconWhite = LoadIcon(hInstance, "icon_white");\r
616   iconBlack = LoadIcon(hInstance, "icon_black");\r
617   iconCurrent = iconWhite;\r
618   InitDrawingColors();\r
619   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
620   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
621   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
622     /* Compute window size for each board size, and use the largest\r
623        size that fits on this screen as the default. */\r
624     InitDrawingSizes((BoardSize)ibs, 0);\r
625     if (boardSize == (BoardSize)-1 &&\r
626         winHeight <= screenHeight\r
627            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
628         && winWidth <= screenWidth) {\r
629       boardSize = (BoardSize)ibs;\r
630     }\r
631   }\r
632   InitDrawingSizes(boardSize, 0);\r
633   InitMenuChecks();\r
634   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
635 \r
636   /* [AS] Load textures if specified */\r
637   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
638   \r
639   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
640       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
641       liteBackTextureMode = appData.liteBackTextureMode;\r
642 \r
643       if (liteBackTexture == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
645       }\r
646   }\r
647   \r
648   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
649       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
650       darkBackTextureMode = appData.darkBackTextureMode;\r
651 \r
652       if (darkBackTexture == NULL && appData.debugMode) {\r
653           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
654       }\r
655   }\r
656 \r
657   mysrandom( (unsigned) time(NULL) );\r
658 \r
659   /* Make a console window if needed */\r
660   if (appData.icsActive) {\r
661     ConsoleCreate();\r
662   }\r
663 \r
664   /* [AS] Restore layout */\r
665   if( wpMoveHistory.visible ) {\r
666       MoveHistoryPopUp();\r
667   }\r
668 \r
669   if( wpEvalGraph.visible ) {\r
670       EvalGraphPopUp();\r
671   }\r
672 \r
673   if( wpEngineOutput.visible ) {\r
674       EngineOutputPopUp();\r
675   }\r
676 \r
677   InitBackEnd2();\r
678 \r
679   /* Make the window visible; update its client area; and return "success" */\r
680   EnsureOnScreen(&boardX, &boardY);\r
681   wp.length = sizeof(WINDOWPLACEMENT);\r
682   wp.flags = 0;\r
683   wp.showCmd = nCmdShow;\r
684   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
685   wp.rcNormalPosition.left = boardX;\r
686   wp.rcNormalPosition.right = boardX + winWidth;\r
687   wp.rcNormalPosition.top = boardY;\r
688   wp.rcNormalPosition.bottom = boardY + winHeight;\r
689   SetWindowPlacement(hwndMain, &wp);\r
690 \r
691   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
692                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
693 \r
694 #if 0\r
695   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
696   if( gameInfo.variant != VariantFischeRandom ) {\r
697       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
698   }\r
699 #endif\r
700   if (hwndConsole) {\r
701 #if AOT_CONSOLE\r
702     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
703                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
704 #endif\r
705     ShowWindow(hwndConsole, nCmdShow);\r
706   }\r
707   UpdateWindow(hwnd);\r
708 \r
709   return TRUE;\r
710 \r
711 }\r
712 \r
713 \r
714 typedef enum {\r
715   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
716   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
717   ArgSettingsFilename\r
718 } ArgType;\r
719 \r
720 typedef struct {\r
721   char *argName;\r
722   ArgType argType;\r
723   /***\r
724   union {\r
725     String *pString;       // ArgString\r
726     int *pInt;             // ArgInt\r
727     float *pFloat;         // ArgFloat\r
728     Boolean *pBoolean;     // ArgBoolean\r
729     COLORREF *pColor;      // ArgColor\r
730     ColorClass cc;         // ArgAttribs\r
731     String *pFilename;     // ArgFilename\r
732     BoardSize *pBoardSize; // ArgBoardSize\r
733     int whichFont;         // ArgFont\r
734     DCB *pDCB;             // ArgCommSettings\r
735     String *pFilename;     // ArgSettingsFilename\r
736   } argLoc;\r
737   ***/\r
738   LPVOID argLoc;\r
739   BOOL save;\r
740 } ArgDescriptor;\r
741 \r
742 int junk;\r
743 ArgDescriptor argDescriptors[] = {\r
744   /* positional arguments */\r
745   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
746   { "", ArgNone, NULL },\r
747   /* keyword arguments */\r
748   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
749   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
750   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
751   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
752   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
753   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
754   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
755   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
756   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
757   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
758   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
759   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
760   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
761   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
762   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
763   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
764   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
765   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
766     FALSE },\r
767   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
768     FALSE },\r
769   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
770     FALSE },\r
771   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
772   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
773     FALSE },\r
774   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
775   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
776   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
779   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
780   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
783   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
784   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
785   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
786   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
787   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
788   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
789   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
790   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
791   /*!!bitmapDirectory?*/\r
792   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
793   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
794   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
795   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
796   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
797   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
798   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
799   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
800   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
801   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
802   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
803   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
804   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
805   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
806   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
807   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
808   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
809   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
810   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
811   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
814   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
815   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
816   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
817   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
820   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
821   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
822   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
823   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
824   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
825   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
826   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
827   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
828   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
829   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
830   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
831   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
833   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
834   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
835   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
836   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
837   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
838   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
839   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
840   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
841   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
842   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
843   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
844   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
845   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
846   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
847   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
848   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
849   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
850   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
851   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
852   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
853   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
854   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
855   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
856   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
857   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
858   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
859   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
860   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
861   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
862   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
863   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
864   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
865   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
866   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
867   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
868   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
869   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
870   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
871   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
873   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
874   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
875   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
877   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
878   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
879   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
881   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
882   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
883   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
885   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
886     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
887   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
888   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
889   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
890   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
891   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
892   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
893   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
894   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
895     TRUE }, /* must come after all fonts */\r
896   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
897   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
898     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
899   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
900   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
901   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
903   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
904   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
905   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
907   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
908   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
909   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
910   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
911   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
912   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
913   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
915   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
916   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
917   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
918   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
919   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
920   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
921   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
923   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
924   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
925   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
927 #if 0\r
928   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
929   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
930 #endif\r
931   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
932   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
933   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
935   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
936   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
937   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
939   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
940   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
941   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
943   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
944   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
945   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
947   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
948   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
949   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
950   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
951   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
952   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
953   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
954   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
955   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
956   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
957   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
959   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
960   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
961   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
962   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
964   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
965   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
966   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
967   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
969   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
970   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
971   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
972   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
973   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
975   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
976   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
977   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
978   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
979   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
980   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
981   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
982   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
984   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
985   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
986   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
987   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
988   { "highlightLastMove", ArgBoolean,\r
989     (LPVOID) &appData.highlightLastMove, TRUE },\r
990   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
991   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
993   { "highlightDragging", ArgBoolean,\r
994     (LPVOID) &appData.highlightDragging, TRUE },\r
995   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
996   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
998   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
999   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1000   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1001   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1002   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1003   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1004   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1005   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1006   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1007   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1008   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1009   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1010   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1011   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1012   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1013   { "soundShout", ArgFilename,\r
1014     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1015   { "soundSShout", ArgFilename,\r
1016     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1017   { "soundChannel1", ArgFilename,\r
1018     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1019   { "soundChannel", ArgFilename,\r
1020     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1021   { "soundKibitz", ArgFilename,\r
1022     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1023   { "soundTell", ArgFilename,\r
1024     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1025   { "soundChallenge", ArgFilename,\r
1026     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1027   { "soundRequest", ArgFilename,\r
1028     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1029   { "soundSeek", ArgFilename,\r
1030     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1031   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1032   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1033   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1034   { "soundIcsLoss", ArgFilename, \r
1035     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1036   { "soundIcsDraw", ArgFilename, \r
1037     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1038   { "soundIcsUnfinished", ArgFilename, \r
1039     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1040   { "soundIcsAlarm", ArgFilename, \r
1041     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1042   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1043   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1046   { "reuseChessPrograms", ArgBoolean,\r
1047     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1048   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1049   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1052   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1053   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1054   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1055   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1056   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1057   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1058   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1059   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1060   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1061   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1062   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1063   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1064   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1065   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1066   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1067   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1068   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1069   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1070   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1071   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1072   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1073   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1074   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1075   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1076   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1077   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1078   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1079   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1080   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1081   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1082   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1083   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1084   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1085     TRUE },\r
1086   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1087     TRUE },\r
1088   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1089   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1090   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1091   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1092   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1093   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1094   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1095   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1097   /* [AS] New features */\r
1098   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1099   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1100   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1101   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1102   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1103   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1104   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1105   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1106   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1107   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1108   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1109   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1110   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1111   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1112   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1113   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1114   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1115   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1116   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1117   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1118   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1119   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1120   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1121   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1122   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1123   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1124   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1125   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1126   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1127   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1128   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1129   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1130   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1131   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1132   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1133   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1134   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1135   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1136   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1137   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1138   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1139   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1140   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1141   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1142   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1143   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1144   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1145   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1146 \r
1147   /* [AS] Layout stuff */\r
1148   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1149   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1150   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1151   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1152   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1153 \r
1154   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1155   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1156   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1157   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1158   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1159 \r
1160   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1161   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1162   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1163   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1164   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1165 \r
1166   /* [HGM] board-size, adjudication and misc. options */\r
1167   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1168   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1169   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1170   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1171   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1172   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1173   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1174   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1175   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1176   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1177   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1178   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1179   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1180   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1181 \r
1182 #ifdef ZIPPY\r
1183   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1184   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1185   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1186   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1187   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1188   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1189   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1190   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1191   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1192   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1193   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1194   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1195   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1196     FALSE },\r
1197   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1198   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1199   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1200   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1201   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1202   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1203   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1204     FALSE },\r
1205   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1206   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1207   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1208   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1209   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1210   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1211   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1212   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1213   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1214   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1215   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1216   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1217   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1218   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1219   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1220   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1221   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1222   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1223 #endif\r
1224   /* [HGM] options for broadcasting and time odds */\r
1225   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1226   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1227   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1228   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1229   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1230   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1231   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1232   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1233   { NULL, ArgNone, NULL, FALSE }\r
1234 };\r
1235 \r
1236 \r
1237 /* Kludge for indirection files on command line */\r
1238 char* lastIndirectionFilename;\r
1239 ArgDescriptor argDescriptorIndirection =\r
1240 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1241 \r
1242 \r
1243 VOID\r
1244 ExitArgError(char *msg, char *badArg)\r
1245 {\r
1246   char buf[MSG_SIZ];\r
1247 \r
1248   sprintf(buf, "%s %s", msg, badArg);\r
1249   DisplayFatalError(buf, 0, 2);\r
1250   exit(2);\r
1251 }\r
1252 \r
1253 /* Command line font name parser.  NULL name means do nothing.\r
1254    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1255    For backward compatibility, syntax without the colon is also\r
1256    accepted, but font names with digits in them won't work in that case.\r
1257 */\r
1258 VOID\r
1259 ParseFontName(char *name, MyFontParams *mfp)\r
1260 {\r
1261   char *p, *q;\r
1262   if (name == NULL) return;\r
1263   p = name;\r
1264   q = strchr(p, ':');\r
1265   if (q) {\r
1266     if (q - p >= sizeof(mfp->faceName))\r
1267       ExitArgError("Font name too long:", name);\r
1268     memcpy(mfp->faceName, p, q - p);\r
1269     mfp->faceName[q - p] = NULLCHAR;\r
1270     p = q + 1;\r
1271   } else {\r
1272     q = mfp->faceName;\r
1273     while (*p && !isdigit(*p)) {\r
1274       *q++ = *p++;\r
1275       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1276         ExitArgError("Font name too long:", name);\r
1277     }\r
1278     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1279     *q = NULLCHAR;\r
1280   }\r
1281   if (!*p) ExitArgError("Font point size missing:", name);\r
1282   mfp->pointSize = (float) atof(p);\r
1283   mfp->bold = (strchr(p, 'b') != NULL);\r
1284   mfp->italic = (strchr(p, 'i') != NULL);\r
1285   mfp->underline = (strchr(p, 'u') != NULL);\r
1286   mfp->strikeout = (strchr(p, 's') != NULL);\r
1287 }\r
1288 \r
1289 /* Color name parser.\r
1290    X version accepts X color names, but this one\r
1291    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1292 COLORREF\r
1293 ParseColorName(char *name)\r
1294 {\r
1295   int red, green, blue, count;\r
1296   char buf[MSG_SIZ];\r
1297 \r
1298   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1299   if (count != 3) {\r
1300     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1301       &red, &green, &blue);\r
1302   }\r
1303   if (count != 3) {\r
1304     sprintf(buf, "Can't parse color name %s", name);\r
1305     DisplayError(buf, 0);\r
1306     return RGB(0, 0, 0);\r
1307   }\r
1308   return PALETTERGB(red, green, blue);\r
1309 }\r
1310 \r
1311 \r
1312 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1313 {\r
1314   char *e = argValue;\r
1315   int eff = 0;\r
1316 \r
1317   while (*e) {\r
1318     if (*e == 'b')      eff |= CFE_BOLD;\r
1319     else if (*e == 'i') eff |= CFE_ITALIC;\r
1320     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1321     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1322     else if (*e == '#' || isdigit(*e)) break;\r
1323     e++;\r
1324   }\r
1325   *effects = eff;\r
1326   *color   = ParseColorName(e);\r
1327 }\r
1328 \r
1329 \r
1330 BoardSize\r
1331 ParseBoardSize(char *name)\r
1332 {\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1336     bs++;\r
1337   }\r
1338   ExitArgError("Unrecognized board size value", name);\r
1339   return bs; /* not reached */\r
1340 }\r
1341 \r
1342 \r
1343 char\r
1344 StringGet(void *getClosure)\r
1345 {\r
1346   char **p = (char **) getClosure;\r
1347   return *((*p)++);\r
1348 }\r
1349 \r
1350 char\r
1351 FileGet(void *getClosure)\r
1352 {\r
1353   int c;\r
1354   FILE* f = (FILE*) getClosure;\r
1355 \r
1356   c = getc(f);\r
1357   if (c == EOF)\r
1358     return NULLCHAR;\r
1359   else\r
1360     return (char) c;\r
1361 }\r
1362 \r
1363 /* Parse settings file named "name". If file found, return the\r
1364    full name in fullname and return TRUE; else return FALSE */\r
1365 BOOLEAN\r
1366 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1367 {\r
1368   char *dummy;\r
1369   FILE *f;\r
1370 \r
1371   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1372     f = fopen(fullname, "r");\r
1373     if (f != NULL) {\r
1374       ParseArgs(FileGet, f);\r
1375       fclose(f);\r
1376       return TRUE;\r
1377     }\r
1378   }\r
1379   return FALSE;\r
1380 }\r
1381 \r
1382 VOID\r
1383 ParseArgs(GetFunc get, void *cl)\r
1384 {\r
1385   char argName[ARG_MAX];\r
1386   char argValue[ARG_MAX];\r
1387   ArgDescriptor *ad;\r
1388   char start;\r
1389   char *q;\r
1390   int i, octval;\r
1391   char ch;\r
1392   int posarg = 0;\r
1393 \r
1394   ch = get(cl);\r
1395   for (;;) {\r
1396     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1397     if (ch == NULLCHAR) break;\r
1398     if (ch == ';') {\r
1399       /* Comment to end of line */\r
1400       ch = get(cl);\r
1401       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1402       continue;\r
1403     } else if (ch == '/' || ch == '-') {\r
1404       /* Switch */\r
1405       q = argName;\r
1406       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1407              ch != '\n' && ch != '\t') {\r
1408         *q++ = ch;\r
1409         ch = get(cl);\r
1410       }\r
1411       *q = NULLCHAR;\r
1412 \r
1413       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1414         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1415 \r
1416       if (ad->argName == NULL)\r
1417         ExitArgError("Unrecognized argument", argName);\r
1418 \r
1419     } else if (ch == '@') {\r
1420       /* Indirection file */\r
1421       ad = &argDescriptorIndirection;\r
1422       ch = get(cl);\r
1423     } else {\r
1424       /* Positional argument */\r
1425       ad = &argDescriptors[posarg++];\r
1426       strcpy(argName, ad->argName);\r
1427     }\r
1428 \r
1429     if (ad->argType == ArgTrue) {\r
1430       *(Boolean *) ad->argLoc = TRUE;\r
1431       continue;\r
1432     }\r
1433     if (ad->argType == ArgFalse) {\r
1434       *(Boolean *) ad->argLoc = FALSE;\r
1435       continue;\r
1436     }\r
1437 \r
1438     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1439     if (ch == NULLCHAR || ch == '\n') {\r
1440       ExitArgError("No value provided for argument", argName);\r
1441     }\r
1442     q = argValue;\r
1443     if (ch == '{') {\r
1444       // Quoting with { }.  No characters have to (or can) be escaped.\r
1445       // Thus the string cannot contain a '}' character.\r
1446       start = ch;\r
1447       ch = get(cl);\r
1448       while (start) {\r
1449         switch (ch) {\r
1450         case NULLCHAR:\r
1451           start = NULLCHAR;\r
1452           break;\r
1453           \r
1454         case '}':\r
1455           ch = get(cl);\r
1456           start = NULLCHAR;\r
1457           break;\r
1458 \r
1459         default:\r
1460           *q++ = ch;\r
1461           ch = get(cl);\r
1462           break;\r
1463         }\r
1464       }   \r
1465     } else if (ch == '\'' || ch == '"') {\r
1466       // Quoting with ' ' or " ", with \ as escape character.\r
1467       // Inconvenient for long strings that may contain Windows filenames.\r
1468       start = ch;\r
1469       ch = get(cl);\r
1470       while (start) {\r
1471         switch (ch) {\r
1472         case NULLCHAR:\r
1473           start = NULLCHAR;\r
1474           break;\r
1475 \r
1476         default:\r
1477         not_special:\r
1478           *q++ = ch;\r
1479           ch = get(cl);\r
1480           break;\r
1481 \r
1482         case '\'':\r
1483         case '\"':\r
1484           if (ch == start) {\r
1485             ch = get(cl);\r
1486             start = NULLCHAR;\r
1487             break;\r
1488           } else {\r
1489             goto not_special;\r
1490           }\r
1491 \r
1492         case '\\':\r
1493           if (ad->argType == ArgFilename\r
1494               || ad->argType == ArgSettingsFilename) {\r
1495               goto not_special;\r
1496           }\r
1497           ch = get(cl);\r
1498           switch (ch) {\r
1499           case NULLCHAR:\r
1500             ExitArgError("Incomplete \\ escape in value for", argName);\r
1501             break;\r
1502           case 'n':\r
1503             *q++ = '\n';\r
1504             ch = get(cl);\r
1505             break;\r
1506           case 'r':\r
1507             *q++ = '\r';\r
1508             ch = get(cl);\r
1509             break;\r
1510           case 't':\r
1511             *q++ = '\t';\r
1512             ch = get(cl);\r
1513             break;\r
1514           case 'b':\r
1515             *q++ = '\b';\r
1516             ch = get(cl);\r
1517             break;\r
1518           case 'f':\r
1519             *q++ = '\f';\r
1520             ch = get(cl);\r
1521             break;\r
1522           default:\r
1523             octval = 0;\r
1524             for (i = 0; i < 3; i++) {\r
1525               if (ch >= '0' && ch <= '7') {\r
1526                 octval = octval*8 + (ch - '0');\r
1527                 ch = get(cl);\r
1528               } else {\r
1529                 break;\r
1530               }\r
1531             }\r
1532             if (i > 0) {\r
1533               *q++ = (char) octval;\r
1534             } else {\r
1535               *q++ = ch;\r
1536               ch = get(cl);\r
1537             }\r
1538             break;\r
1539           }\r
1540           break;\r
1541         }\r
1542       }\r
1543     } else {\r
1544       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1545         *q++ = ch;\r
1546         ch = get(cl);\r
1547       }\r
1548     }\r
1549     *q = NULLCHAR;\r
1550 \r
1551     switch (ad->argType) {\r
1552     case ArgInt:\r
1553       *(int *) ad->argLoc = atoi(argValue);\r
1554       break;\r
1555 \r
1556     case ArgFloat:\r
1557       *(float *) ad->argLoc = (float) atof(argValue);\r
1558       break;\r
1559 \r
1560     case ArgString:\r
1561     case ArgFilename:\r
1562       *(char **) ad->argLoc = strdup(argValue);\r
1563       break;\r
1564 \r
1565     case ArgSettingsFilename:\r
1566       {\r
1567         char fullname[MSG_SIZ];\r
1568         if (ParseSettingsFile(argValue, fullname)) {\r
1569           if (ad->argLoc != NULL) {\r
1570             *(char **) ad->argLoc = strdup(fullname);\r
1571           }\r
1572         } else {\r
1573           if (ad->argLoc != NULL) {\r
1574           } else {\r
1575             ExitArgError("Failed to open indirection file", argValue);\r
1576           }\r
1577         }\r
1578       }\r
1579       break;\r
1580 \r
1581     case ArgBoolean:\r
1582       switch (argValue[0]) {\r
1583       case 't':\r
1584       case 'T':\r
1585         *(Boolean *) ad->argLoc = TRUE;\r
1586         break;\r
1587       case 'f':\r
1588       case 'F':\r
1589         *(Boolean *) ad->argLoc = FALSE;\r
1590         break;\r
1591       default:\r
1592         ExitArgError("Unrecognized boolean argument value", argValue);\r
1593         break;\r
1594       }\r
1595       break;\r
1596 \r
1597     case ArgColor:\r
1598       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1599       break;\r
1600 \r
1601     case ArgAttribs: {\r
1602       ColorClass cc = (ColorClass)ad->argLoc;\r
1603       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1604       }\r
1605       break;\r
1606       \r
1607     case ArgBoardSize:\r
1608       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1609       break;\r
1610 \r
1611     case ArgFont:\r
1612       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1613       break;\r
1614 \r
1615     case ArgCommSettings:\r
1616       ParseCommSettings(argValue, &dcb);\r
1617       break;\r
1618 \r
1619     case ArgNone:\r
1620       ExitArgError("Unrecognized argument", argValue);\r
1621       break;\r
1622     }\r
1623   }\r
1624 }\r
1625 \r
1626 VOID\r
1627 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1628 {\r
1629   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1630   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1631   DeleteDC(hdc);\r
1632   lf->lfWidth = 0;\r
1633   lf->lfEscapement = 0;\r
1634   lf->lfOrientation = 0;\r
1635   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1636   lf->lfItalic = mfp->italic;\r
1637   lf->lfUnderline = mfp->underline;\r
1638   lf->lfStrikeOut = mfp->strikeout;\r
1639   lf->lfCharSet = DEFAULT_CHARSET;\r
1640   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1641   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1642   lf->lfQuality = DEFAULT_QUALITY;\r
1643   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1644   strcpy(lf->lfFaceName, mfp->faceName);\r
1645 }\r
1646 \r
1647 VOID\r
1648 CreateFontInMF(MyFont *mf)\r
1649 {\r
1650   LFfromMFP(&mf->lf, &mf->mfp);\r
1651   if (mf->hf) DeleteObject(mf->hf);\r
1652   mf->hf = CreateFontIndirect(&mf->lf);\r
1653 }\r
1654 \r
1655 VOID\r
1656 SetDefaultTextAttribs()\r
1657 {\r
1658   ColorClass cc;\r
1659   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1660     ParseAttribs(&textAttribs[cc].color, \r
1661                  &textAttribs[cc].effects, \r
1662                  defaultTextAttribs[cc]);\r
1663   }\r
1664 }\r
1665 \r
1666 VOID\r
1667 SetDefaultSounds()\r
1668 {\r
1669   ColorClass cc;\r
1670   SoundClass sc;\r
1671   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1672     textAttribs[cc].sound.name = strdup("");\r
1673     textAttribs[cc].sound.data = NULL;\r
1674   }\r
1675   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1676     sounds[sc].name = strdup("");\r
1677     sounds[sc].data = NULL;\r
1678   }\r
1679   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1680 }\r
1681 \r
1682 VOID\r
1683 LoadAllSounds()\r
1684 {\r
1685   ColorClass cc;\r
1686   SoundClass sc;\r
1687   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1688     MyLoadSound(&textAttribs[cc].sound);\r
1689   }\r
1690   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1691     MyLoadSound(&sounds[sc]);\r
1692   }\r
1693 }\r
1694 \r
1695 VOID\r
1696 InitAppData(LPSTR lpCmdLine)\r
1697 {\r
1698   int i, j;\r
1699   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1700   char *dummy, *p;\r
1701 \r
1702   programName = szAppName;\r
1703 \r
1704   /* Initialize to defaults */\r
1705   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1706   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1707   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1708   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1709   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1710   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1711   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1712   SetDefaultTextAttribs();\r
1713   SetDefaultSounds();\r
1714   appData.movesPerSession = MOVES_PER_SESSION;\r
1715   appData.initString = INIT_STRING;\r
1716   appData.secondInitString = INIT_STRING;\r
1717   appData.firstComputerString = COMPUTER_STRING;\r
1718   appData.secondComputerString = COMPUTER_STRING;\r
1719   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1720   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1721   appData.firstPlaysBlack = FALSE;\r
1722   appData.noChessProgram = FALSE;\r
1723   chessProgram = FALSE;\r
1724   appData.firstHost = FIRST_HOST;\r
1725   appData.secondHost = SECOND_HOST;\r
1726   appData.firstDirectory = FIRST_DIRECTORY;\r
1727   appData.secondDirectory = SECOND_DIRECTORY;\r
1728   appData.bitmapDirectory = "";\r
1729   appData.remoteShell = REMOTE_SHELL;\r
1730   appData.remoteUser = "";\r
1731   appData.timeDelay = TIME_DELAY;\r
1732   appData.timeControl = TIME_CONTROL;\r
1733   appData.timeIncrement = TIME_INCREMENT;\r
1734   appData.icsActive = FALSE;\r
1735   appData.icsHost = "";\r
1736   appData.icsPort = ICS_PORT;\r
1737   appData.icsCommPort = ICS_COMM_PORT;\r
1738   appData.icsLogon = ICS_LOGON;\r
1739   appData.icsHelper = "";\r
1740   appData.useTelnet = FALSE;\r
1741   appData.telnetProgram = TELNET_PROGRAM;\r
1742   appData.gateway = "";\r
1743   appData.loadGameFile = "";\r
1744   appData.loadGameIndex = 0;\r
1745   appData.saveGameFile = "";\r
1746   appData.autoSaveGames = FALSE;\r
1747   appData.loadPositionFile = "";\r
1748   appData.loadPositionIndex = 1;\r
1749   appData.savePositionFile = "";\r
1750   appData.matchMode = FALSE;\r
1751   appData.matchGames = 0;\r
1752   appData.monoMode = FALSE;\r
1753   appData.debugMode = FALSE;\r
1754   appData.clockMode = TRUE;\r
1755   boardSize = (BoardSize) -1; /* determine by screen size */\r
1756   appData.Iconic = FALSE; /*unused*/\r
1757   appData.searchTime = "";\r
1758   appData.searchDepth = 0;\r
1759   appData.showCoords = FALSE;\r
1760   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1761   appData.autoCallFlag = FALSE;\r
1762   appData.flipView = FALSE;\r
1763   appData.autoFlipView = TRUE;\r
1764   appData.cmailGameName = "";\r
1765   appData.alwaysPromoteToQueen = FALSE;\r
1766   appData.oldSaveStyle = FALSE;\r
1767   appData.quietPlay = FALSE;\r
1768   appData.showThinking = FALSE;\r
1769   appData.ponderNextMove = TRUE;\r
1770   appData.periodicUpdates = TRUE;\r
1771   appData.popupExitMessage = TRUE;\r
1772   appData.popupMoveErrors = FALSE;\r
1773   appData.autoObserve = FALSE;\r
1774   appData.autoComment = FALSE;\r
1775   appData.animate = TRUE;\r
1776   appData.animSpeed = 10;\r
1777   appData.animateDragging = TRUE;\r
1778   appData.highlightLastMove = TRUE;\r
1779   appData.getMoveList = TRUE;\r
1780   appData.testLegality = TRUE;\r
1781   appData.premove = TRUE;\r
1782   appData.premoveWhite = FALSE;\r
1783   appData.premoveWhiteText = "";\r
1784   appData.premoveBlack = FALSE;\r
1785   appData.premoveBlackText = "";\r
1786   appData.icsAlarm = TRUE;\r
1787   appData.icsAlarmTime = 5000;\r
1788   appData.autoRaiseBoard = TRUE;\r
1789   appData.localLineEditing = TRUE;\r
1790   appData.colorize = TRUE;\r
1791   appData.reuseFirst = TRUE;\r
1792   appData.reuseSecond = TRUE;\r
1793   appData.blindfold = FALSE;\r
1794   dcb.DCBlength = sizeof(DCB);\r
1795   dcb.BaudRate = 9600;\r
1796   dcb.fBinary = TRUE;\r
1797   dcb.fParity = FALSE;\r
1798   dcb.fOutxCtsFlow = FALSE;\r
1799   dcb.fOutxDsrFlow = FALSE;\r
1800   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1801   dcb.fDsrSensitivity = FALSE;\r
1802   dcb.fTXContinueOnXoff = TRUE;\r
1803   dcb.fOutX = FALSE;\r
1804   dcb.fInX = FALSE;\r
1805   dcb.fNull = FALSE;\r
1806   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1807   dcb.fAbortOnError = FALSE;\r
1808   dcb.wReserved = 0;\r
1809   dcb.ByteSize = 7;\r
1810   dcb.Parity = SPACEPARITY;\r
1811   dcb.StopBits = ONESTOPBIT;\r
1812   settingsFileName = SETTINGS_FILE;\r
1813   saveSettingsOnExit = TRUE;\r
1814   boardX = CW_USEDEFAULT;\r
1815   boardY = CW_USEDEFAULT;\r
1816   consoleX = CW_USEDEFAULT; \r
1817   consoleY = CW_USEDEFAULT; \r
1818   consoleW = CW_USEDEFAULT;\r
1819   consoleH = CW_USEDEFAULT;\r
1820   analysisX = CW_USEDEFAULT; \r
1821   analysisY = CW_USEDEFAULT; \r
1822   analysisW = CW_USEDEFAULT;\r
1823   analysisH = CW_USEDEFAULT;\r
1824   commentX = CW_USEDEFAULT; \r
1825   commentY = CW_USEDEFAULT; \r
1826   commentW = CW_USEDEFAULT;\r
1827   commentH = CW_USEDEFAULT;\r
1828   editTagsX = CW_USEDEFAULT; \r
1829   editTagsY = CW_USEDEFAULT; \r
1830   editTagsW = CW_USEDEFAULT;\r
1831   editTagsH = CW_USEDEFAULT;\r
1832   gameListX = CW_USEDEFAULT; \r
1833   gameListY = CW_USEDEFAULT; \r
1834   gameListW = CW_USEDEFAULT;\r
1835   gameListH = CW_USEDEFAULT;\r
1836   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1837   icsNames = ICS_NAMES;\r
1838   firstChessProgramNames = FCP_NAMES;\r
1839   secondChessProgramNames = SCP_NAMES;\r
1840   appData.initialMode = "";\r
1841   appData.variant = "normal";\r
1842   appData.firstProtocolVersion = PROTOVER;\r
1843   appData.secondProtocolVersion = PROTOVER;\r
1844   appData.showButtonBar = TRUE;\r
1845 \r
1846    /* [AS] New properties (see comments in header file) */\r
1847   appData.firstScoreIsAbsolute = FALSE;\r
1848   appData.secondScoreIsAbsolute = FALSE;\r
1849   appData.saveExtendedInfoInPGN = FALSE;\r
1850   appData.hideThinkingFromHuman = FALSE;\r
1851   appData.liteBackTextureFile = "";\r
1852   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1853   appData.darkBackTextureFile = "";\r
1854   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1855   appData.renderPiecesWithFont = "";\r
1856   appData.fontToPieceTable = "";\r
1857   appData.fontBackColorWhite = 0;\r
1858   appData.fontForeColorWhite = 0;\r
1859   appData.fontBackColorBlack = 0;\r
1860   appData.fontForeColorBlack = 0;\r
1861   appData.fontPieceSize = 80;\r
1862   appData.overrideLineGap = 1;\r
1863   appData.adjudicateLossThreshold = 0;\r
1864   appData.delayBeforeQuit = 0;\r
1865   appData.delayAfterQuit = 0;\r
1866   appData.nameOfDebugFile = "winboard.debug";\r
1867   appData.pgnEventHeader = "Computer Chess Game";\r
1868   appData.defaultFrcPosition = -1;\r
1869   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1870   appData.saveOutOfBookInfo = TRUE;\r
1871   appData.showEvalInMoveHistory = TRUE;\r
1872   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1873   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1874   appData.highlightMoveWithArrow = FALSE;\r
1875   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1876   appData.useStickyWindows = TRUE;\r
1877   appData.adjudicateDrawMoves = 0;\r
1878   appData.autoDisplayComment = TRUE;\r
1879   appData.autoDisplayTags = TRUE;\r
1880   appData.firstIsUCI = FALSE;\r
1881   appData.secondIsUCI = FALSE;\r
1882   appData.firstHasOwnBookUCI = TRUE;\r
1883   appData.secondHasOwnBookUCI = TRUE;\r
1884   appData.polyglotDir = "";\r
1885   appData.usePolyglotBook = FALSE;\r
1886   appData.polyglotBook = "";\r
1887   appData.defaultHashSize = 64;\r
1888   appData.defaultCacheSizeEGTB = 4;\r
1889   appData.defaultPathEGTB = "c:\\egtb";\r
1890 \r
1891   InitWindowPlacement( &wpMoveHistory );\r
1892   InitWindowPlacement( &wpEvalGraph );\r
1893   InitWindowPlacement( &wpEngineOutput );\r
1894 \r
1895   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1896   appData.NrFiles      = -1;\r
1897   appData.NrRanks      = -1;\r
1898   appData.holdingsSize = -1;\r
1899   appData.testClaims   = FALSE;\r
1900   appData.checkMates   = FALSE;\r
1901   appData.materialDraws= FALSE;\r
1902   appData.trivialDraws = FALSE;\r
1903   appData.ruleMoves    = 51;\r
1904   appData.drawRepeats  = 6;\r
1905   appData.matchPause   = 10000;\r
1906   appData.alphaRank    = FALSE;\r
1907   appData.allWhite     = FALSE;\r
1908   appData.upsideDown   = FALSE;\r
1909   appData.serverPause  = 15;\r
1910   appData.serverMovesName   = NULL;\r
1911   appData.suppressLoadMoves = FALSE;\r
1912   appData.firstTimeOdds  = 1;\r
1913   appData.secondTimeOdds = 1;\r
1914   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1915   appData.secondAccumulateTC = 1;\r
1916 \r
1917 #ifdef ZIPPY\r
1918   appData.zippyTalk = ZIPPY_TALK;\r
1919   appData.zippyPlay = ZIPPY_PLAY;\r
1920   appData.zippyLines = ZIPPY_LINES;\r
1921   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1922   appData.zippyPassword = ZIPPY_PASSWORD;\r
1923   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1924   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1925   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1926   appData.zippyUseI = ZIPPY_USE_I;\r
1927   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1928   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1929   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1930   appData.zippyGameStart = ZIPPY_GAME_START;\r
1931   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1932   appData.zippyAbort = ZIPPY_ABORT;\r
1933   appData.zippyVariants = ZIPPY_VARIANTS;\r
1934   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1935   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1936 #endif\r
1937 \r
1938   /* Point font array elements to structures and\r
1939      parse default font names */\r
1940   for (i=0; i<NUM_FONTS; i++) {\r
1941     for (j=0; j<NUM_SIZES; j++) {\r
1942       font[j][i] = &fontRec[j][i];\r
1943       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1944     }\r
1945   }\r
1946   \r
1947   /* Parse default settings file if any */\r
1948   if (ParseSettingsFile(settingsFileName, buf)) {\r
1949     settingsFileName = strdup(buf);\r
1950   }\r
1951 \r
1952   /* Parse command line */\r
1953   ParseArgs(StringGet, &lpCmdLine);\r
1954 \r
1955   /* [HGM] make sure board size is acceptable */\r
1956   if(appData.NrFiles > BOARD_SIZE ||\r
1957      appData.NrRanks > BOARD_SIZE   )\r
1958       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1959 \r
1960   /* [HGM] After parsing the options from the .ini file, and overruling them\r
1961    * with options from the command line, we now make an even higher priority\r
1962    * overrule by WB options attached to the engine command line. This so that\r
1963    * tournament managers can use WB options (such as /timeOdds) that follow\r
1964    * the engines.\r
1965    */\r
1966   if(appData.firstChessProgram != NULL) {\r
1967       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
1968       static char *f = "first";\r
1969       char buf[MSG_SIZ], *q = buf;\r
1970       if(p != NULL) { // engine command line contains WinBoard options\r
1971           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
1972           ParseArgs(StringGet, &q);\r
1973           p[-1] = 0; // cut them offengine command line\r
1974       }\r
1975   }\r
1976   // now do same for second chess program\r
1977   if(appData.secondChessProgram != NULL) {\r
1978       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
1979       static char *s = "second";\r
1980       char buf[MSG_SIZ], *q = buf;\r
1981       if(p != NULL) { // engine command line contains WinBoard options\r
1982           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
1983           ParseArgs(StringGet, &q);\r
1984           p[-1] = 0; // cut them offengine command line\r
1985       }\r
1986   }\r
1987 \r
1988 \r
1989   /* Propagate options that affect others */\r
1990   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1991   if (appData.icsActive || appData.noChessProgram) {\r
1992      chessProgram = FALSE;  /* not local chess program mode */\r
1993   }\r
1994 \r
1995   /* Open startup dialog if needed */\r
1996   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1997       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1998       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1999                         *appData.secondChessProgram == NULLCHAR))) {\r
2000     FARPROC lpProc;\r
2001     \r
2002     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2003     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2004     FreeProcInstance(lpProc);\r
2005   }\r
2006 \r
2007   /* Make sure save files land in the right (?) directory */\r
2008   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2009     appData.saveGameFile = strdup(buf);\r
2010   }\r
2011   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2012     appData.savePositionFile = strdup(buf);\r
2013   }\r
2014 \r
2015   /* Finish initialization for fonts and sounds */\r
2016   for (i=0; i<NUM_FONTS; i++) {\r
2017     for (j=0; j<NUM_SIZES; j++) {\r
2018       CreateFontInMF(font[j][i]);\r
2019     }\r
2020   }\r
2021   /* xboard, and older WinBoards, controlled the move sound with the\r
2022      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2023      always turn the option on (so that the backend will call us),\r
2024      then let the user turn the sound off by setting it to silence if\r
2025      desired.  To accommodate old winboard.ini files saved by old\r
2026      versions of WinBoard, we also turn off the sound if the option\r
2027      was initially set to false. */\r
2028   if (!appData.ringBellAfterMoves) {\r
2029     sounds[(int)SoundMove].name = strdup("");\r
2030     appData.ringBellAfterMoves = TRUE;\r
2031   }\r
2032   GetCurrentDirectory(MSG_SIZ, currDir);\r
2033   SetCurrentDirectory(installDir);\r
2034   LoadAllSounds();\r
2035   SetCurrentDirectory(currDir);\r
2036 \r
2037   p = icsTextMenuString;\r
2038   if (p[0] == '@') {\r
2039     FILE* f = fopen(p + 1, "r");\r
2040     if (f == NULL) {\r
2041       DisplayFatalError(p + 1, errno, 2);\r
2042       return;\r
2043     }\r
2044     i = fread(buf, 1, sizeof(buf)-1, f);\r
2045     fclose(f);\r
2046     buf[i] = NULLCHAR;\r
2047     p = buf;\r
2048   }\r
2049   ParseIcsTextMenu(strdup(p));\r
2050 }\r
2051 \r
2052 \r
2053 VOID\r
2054 InitMenuChecks()\r
2055 {\r
2056   HMENU hmenu = GetMenu(hwndMain);\r
2057 \r
2058   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2059                         MF_BYCOMMAND|((appData.icsActive &&\r
2060                                        *appData.icsCommPort != NULLCHAR) ?\r
2061                                       MF_ENABLED : MF_GRAYED));\r
2062   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2063                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2064                                      MF_CHECKED : MF_UNCHECKED));\r
2065 }\r
2066 \r
2067 \r
2068 VOID\r
2069 SaveSettings(char* name)\r
2070 {\r
2071   FILE *f;\r
2072   ArgDescriptor *ad;\r
2073   WINDOWPLACEMENT wp;\r
2074   char dir[MSG_SIZ];\r
2075 \r
2076   if (!hwndMain) return;\r
2077 \r
2078   GetCurrentDirectory(MSG_SIZ, dir);\r
2079   SetCurrentDirectory(installDir);\r
2080   f = fopen(name, "w");\r
2081   SetCurrentDirectory(dir);\r
2082   if (f == NULL) {\r
2083     DisplayError(name, errno);\r
2084     return;\r
2085   }\r
2086   fprintf(f, ";\n");\r
2087   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2088   fprintf(f, ";\n");\r
2089   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2090   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2091   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2092   fprintf(f, ";\n");\r
2093 \r
2094   wp.length = sizeof(WINDOWPLACEMENT);\r
2095   GetWindowPlacement(hwndMain, &wp);\r
2096   boardX = wp.rcNormalPosition.left;\r
2097   boardY = wp.rcNormalPosition.top;\r
2098 \r
2099   if (hwndConsole) {\r
2100     GetWindowPlacement(hwndConsole, &wp);\r
2101     consoleX = wp.rcNormalPosition.left;\r
2102     consoleY = wp.rcNormalPosition.top;\r
2103     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2104     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2105   }\r
2106 \r
2107   if (analysisDialog) {\r
2108     GetWindowPlacement(analysisDialog, &wp);\r
2109     analysisX = wp.rcNormalPosition.left;\r
2110     analysisY = wp.rcNormalPosition.top;\r
2111     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2112     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2113   }\r
2114 \r
2115   if (commentDialog) {\r
2116     GetWindowPlacement(commentDialog, &wp);\r
2117     commentX = wp.rcNormalPosition.left;\r
2118     commentY = wp.rcNormalPosition.top;\r
2119     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2120     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2121   }\r
2122 \r
2123   if (editTagsDialog) {\r
2124     GetWindowPlacement(editTagsDialog, &wp);\r
2125     editTagsX = wp.rcNormalPosition.left;\r
2126     editTagsY = wp.rcNormalPosition.top;\r
2127     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2128     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2129   }\r
2130 \r
2131   if (gameListDialog) {\r
2132     GetWindowPlacement(gameListDialog, &wp);\r
2133     gameListX = wp.rcNormalPosition.left;\r
2134     gameListY = wp.rcNormalPosition.top;\r
2135     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2136     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2137   }\r
2138 \r
2139   /* [AS] Move history */\r
2140   wpMoveHistory.visible = MoveHistoryIsUp();\r
2141   \r
2142   if( moveHistoryDialog ) {\r
2143     GetWindowPlacement(moveHistoryDialog, &wp);\r
2144     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2145     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2146     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2147     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2148   }\r
2149 \r
2150   /* [AS] Eval graph */\r
2151   wpEvalGraph.visible = EvalGraphIsUp();\r
2152 \r
2153   if( evalGraphDialog ) {\r
2154     GetWindowPlacement(evalGraphDialog, &wp);\r
2155     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2156     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2157     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2158     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2159   }\r
2160 \r
2161   /* [AS] Engine output */\r
2162   wpEngineOutput.visible = EngineOutputIsUp();\r
2163 \r
2164   if( engineOutputDialog ) {\r
2165     GetWindowPlacement(engineOutputDialog, &wp);\r
2166     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2167     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2168     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2169     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2170   }\r
2171 \r
2172   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2173     if (!ad->save) continue;\r
2174     switch (ad->argType) {\r
2175     case ArgString:\r
2176       {\r
2177         char *p = *(char **)ad->argLoc;\r
2178         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2179           /* Quote multiline values or \-containing values\r
2180              with { } if possible */\r
2181           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2182         } else {\r
2183           /* Else quote with " " */\r
2184           fprintf(f, "/%s=\"", ad->argName);\r
2185           while (*p) {\r
2186             if (*p == '\n') fprintf(f, "\n");\r
2187             else if (*p == '\r') fprintf(f, "\\r");\r
2188             else if (*p == '\t') fprintf(f, "\\t");\r
2189             else if (*p == '\b') fprintf(f, "\\b");\r
2190             else if (*p == '\f') fprintf(f, "\\f");\r
2191             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2192             else if (*p == '\"') fprintf(f, "\\\"");\r
2193             else if (*p == '\\') fprintf(f, "\\\\");\r
2194             else putc(*p, f);\r
2195             p++;\r
2196           }\r
2197           fprintf(f, "\"\n");\r
2198         }\r
2199       }\r
2200       break;\r
2201     case ArgInt:\r
2202       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2203       break;\r
2204     case ArgFloat:\r
2205       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2206       break;\r
2207     case ArgBoolean:\r
2208       fprintf(f, "/%s=%s\n", ad->argName, \r
2209         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2210       break;\r
2211     case ArgTrue:\r
2212       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2213       break;\r
2214     case ArgFalse:\r
2215       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2216       break;\r
2217     case ArgColor:\r
2218       {\r
2219         COLORREF color = *(COLORREF *)ad->argLoc;\r
2220         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2221           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2222       }\r
2223       break;\r
2224     case ArgAttribs:\r
2225       {\r
2226         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2227         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2228           (ta->effects & CFE_BOLD) ? "b" : "",\r
2229           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2230           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2231           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2232           (ta->effects) ? " " : "",\r
2233           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2234       }\r
2235       break;\r
2236     case ArgFilename:\r
2237       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2238         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2239       } else {\r
2240         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2241       }\r
2242       break;\r
2243     case ArgBoardSize:\r
2244       fprintf(f, "/%s=%s\n", ad->argName,\r
2245               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2246       break;\r
2247     case ArgFont:\r
2248       {\r
2249         int bs;\r
2250         for (bs=0; bs<NUM_SIZES; bs++) {\r
2251           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2252           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2253           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2254             ad->argName, mfp->faceName, mfp->pointSize,\r
2255             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2256             mfp->bold ? "b" : "",\r
2257             mfp->italic ? "i" : "",\r
2258             mfp->underline ? "u" : "",\r
2259             mfp->strikeout ? "s" : "");\r
2260         }\r
2261       }\r
2262       break;\r
2263     case ArgCommSettings:\r
2264       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2265     }\r
2266   }\r
2267   fclose(f);\r
2268 }\r
2269 \r
2270 \r
2271 \r
2272 /*---------------------------------------------------------------------------*\\r
2273  *\r
2274  * GDI board drawing routines\r
2275  *\r
2276 \*---------------------------------------------------------------------------*/\r
2277 \r
2278 /* [AS] Draw square using background texture */\r
2279 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2280 {\r
2281     XFORM   x;\r
2282 \r
2283     if( mode == 0 ) {\r
2284         return; /* Should never happen! */\r
2285     }\r
2286 \r
2287     SetGraphicsMode( dst, GM_ADVANCED );\r
2288 \r
2289     switch( mode ) {\r
2290     case 1:\r
2291         /* Identity */\r
2292         break;\r
2293     case 2:\r
2294         /* X reflection */\r
2295         x.eM11 = -1.0;\r
2296         x.eM12 = 0;\r
2297         x.eM21 = 0;\r
2298         x.eM22 = 1.0;\r
2299         x.eDx = (FLOAT) dw + dx - 1;\r
2300         x.eDy = 0;\r
2301         dx = 0;\r
2302         SetWorldTransform( dst, &x );\r
2303         break;\r
2304     case 3:\r
2305         /* Y reflection */\r
2306         x.eM11 = 1.0;\r
2307         x.eM12 = 0;\r
2308         x.eM21 = 0;\r
2309         x.eM22 = -1.0;\r
2310         x.eDx = 0;\r
2311         x.eDy = (FLOAT) dh + dy - 1;\r
2312         dy = 0;\r
2313         SetWorldTransform( dst, &x );\r
2314         break;\r
2315     case 4:\r
2316         /* X/Y flip */\r
2317         x.eM11 = 0;\r
2318         x.eM12 = 1.0;\r
2319         x.eM21 = 1.0;\r
2320         x.eM22 = 0;\r
2321         x.eDx = (FLOAT) dx;\r
2322         x.eDy = (FLOAT) dy;\r
2323         dx = 0;\r
2324         dy = 0;\r
2325         SetWorldTransform( dst, &x );\r
2326         break;\r
2327     }\r
2328 \r
2329     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2330 \r
2331     x.eM11 = 1.0;\r
2332     x.eM12 = 0;\r
2333     x.eM21 = 0;\r
2334     x.eM22 = 1.0;\r
2335     x.eDx = 0;\r
2336     x.eDy = 0;\r
2337     SetWorldTransform( dst, &x );\r
2338 \r
2339     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2340 }\r
2341 \r
2342 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2343 enum {\r
2344     PM_WP = (int) WhitePawn, \r
2345     PM_WN = (int) WhiteKnight, \r
2346     PM_WB = (int) WhiteBishop, \r
2347     PM_WR = (int) WhiteRook, \r
2348     PM_WQ = (int) WhiteQueen, \r
2349     PM_WF = (int) WhiteFerz, \r
2350     PM_WW = (int) WhiteWazir, \r
2351     PM_WE = (int) WhiteAlfil, \r
2352     PM_WM = (int) WhiteMan, \r
2353     PM_WO = (int) WhiteCannon, \r
2354     PM_WU = (int) WhiteUnicorn, \r
2355     PM_WH = (int) WhiteNightrider, \r
2356     PM_WA = (int) WhiteAngel, \r
2357     PM_WC = (int) WhiteMarshall, \r
2358     PM_WG = (int) WhiteGrasshopper, \r
2359     PM_WK = (int) WhiteKing,\r
2360     PM_BP = (int) BlackPawn, \r
2361     PM_BN = (int) BlackKnight, \r
2362     PM_BB = (int) BlackBishop, \r
2363     PM_BR = (int) BlackRook, \r
2364     PM_BQ = (int) BlackQueen, \r
2365     PM_BF = (int) BlackFerz, \r
2366     PM_BW = (int) BlackWazir, \r
2367     PM_BE = (int) BlackAlfil, \r
2368     PM_BM = (int) BlackMan,\r
2369     PM_BO = (int) BlackCannon, \r
2370     PM_BU = (int) BlackUnicorn, \r
2371     PM_BH = (int) BlackNightrider, \r
2372     PM_BA = (int) BlackAngel, \r
2373     PM_BC = (int) BlackMarshall, \r
2374     PM_BG = (int) BlackGrasshopper, \r
2375     PM_BK = (int) BlackKing\r
2376 };\r
2377 \r
2378 static HFONT hPieceFont = NULL;\r
2379 static HBITMAP hPieceMask[(int) EmptySquare];\r
2380 static HBITMAP hPieceFace[(int) EmptySquare];\r
2381 static int fontBitmapSquareSize = 0;\r
2382 static char pieceToFontChar[(int) EmptySquare] =\r
2383                               { 'p', 'n', 'b', 'r', 'q', \r
2384                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2385                       'k', 'o', 'm', 'v', 't', 'w', \r
2386                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2387                                                               'l' };\r
2388 \r
2389 extern BOOL SetCharTable( char *table, const char * map );\r
2390 /* [HGM] moved to backend.c */\r
2391 \r
2392 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2393 {\r
2394     HBRUSH hbrush;\r
2395     BYTE r1 = GetRValue( color );\r
2396     BYTE g1 = GetGValue( color );\r
2397     BYTE b1 = GetBValue( color );\r
2398     BYTE r2 = r1 / 2;\r
2399     BYTE g2 = g1 / 2;\r
2400     BYTE b2 = b1 / 2;\r
2401     RECT rc;\r
2402 \r
2403     /* Create a uniform background first */\r
2404     hbrush = CreateSolidBrush( color );\r
2405     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2406     FillRect( hdc, &rc, hbrush );\r
2407     DeleteObject( hbrush );\r
2408     \r
2409     if( mode == 1 ) {\r
2410         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2411         int steps = squareSize / 2;\r
2412         int i;\r
2413 \r
2414         for( i=0; i<steps; i++ ) {\r
2415             BYTE r = r1 - (r1-r2) * i / steps;\r
2416             BYTE g = g1 - (g1-g2) * i / steps;\r
2417             BYTE b = b1 - (b1-b2) * i / steps;\r
2418 \r
2419             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2420             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2421             FillRect( hdc, &rc, hbrush );\r
2422             DeleteObject(hbrush);\r
2423         }\r
2424     }\r
2425     else if( mode == 2 ) {\r
2426         /* Diagonal gradient, good more or less for every piece */\r
2427         POINT triangle[3];\r
2428         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2429         HBRUSH hbrush_old;\r
2430         int steps = squareSize;\r
2431         int i;\r
2432 \r
2433         triangle[0].x = squareSize - steps;\r
2434         triangle[0].y = squareSize;\r
2435         triangle[1].x = squareSize;\r
2436         triangle[1].y = squareSize;\r
2437         triangle[2].x = squareSize;\r
2438         triangle[2].y = squareSize - steps;\r
2439 \r
2440         for( i=0; i<steps; i++ ) {\r
2441             BYTE r = r1 - (r1-r2) * i / steps;\r
2442             BYTE g = g1 - (g1-g2) * i / steps;\r
2443             BYTE b = b1 - (b1-b2) * i / steps;\r
2444 \r
2445             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2446             hbrush_old = SelectObject( hdc, hbrush );\r
2447             Polygon( hdc, triangle, 3 );\r
2448             SelectObject( hdc, hbrush_old );\r
2449             DeleteObject(hbrush);\r
2450             triangle[0].x++;\r
2451             triangle[2].y++;\r
2452         }\r
2453 \r
2454         SelectObject( hdc, hpen );\r
2455     }\r
2456 }\r
2457 \r
2458 /*\r
2459     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2460     seems to work ok. The main problem here is to find the "inside" of a chess\r
2461     piece: follow the steps as explained below.\r
2462 */\r
2463 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2464 {\r
2465     HBITMAP hbm;\r
2466     HBITMAP hbm_old;\r
2467     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2468     RECT rc;\r
2469     SIZE sz;\r
2470     POINT pt;\r
2471     int backColor = whitePieceColor; \r
2472     int foreColor = blackPieceColor;\r
2473     int shapeIndex = index < 6 ? index+6 : index;\r
2474     \r
2475     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2476         backColor = appData.fontBackColorWhite;\r
2477         foreColor = appData.fontForeColorWhite;\r
2478     }\r
2479     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2480         backColor = appData.fontBackColorBlack;\r
2481         foreColor = appData.fontForeColorBlack;\r
2482     }\r
2483 \r
2484     /* Mask */\r
2485     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2486 \r
2487     hbm_old = SelectObject( hdc, hbm );\r
2488 \r
2489     rc.left = 0;\r
2490     rc.top = 0;\r
2491     rc.right = squareSize;\r
2492     rc.bottom = squareSize;\r
2493 \r
2494     /* Step 1: background is now black */\r
2495     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2496 \r
2497     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2498 \r
2499     pt.x = (squareSize - sz.cx) / 2;\r
2500     pt.y = (squareSize - sz.cy) / 2;\r
2501 \r
2502     SetBkMode( hdc, TRANSPARENT );\r
2503     SetTextColor( hdc, chroma );\r
2504     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2505     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2506 \r
2507     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2508     /* Step 3: the area outside the piece is filled with white */\r
2509     FloodFill( hdc, 0, 0, chroma );\r
2510     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2511     /* \r
2512         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2513         but if the start point is not inside the piece we're lost!\r
2514         There should be a better way to do this... if we could create a region or path\r
2515         from the fill operation we would be fine for example.\r
2516     */\r
2517     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2518 \r
2519     SetTextColor( hdc, 0 );\r
2520     /* \r
2521         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2522         draw the piece again in black for safety.\r
2523     */\r
2524     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2525 \r
2526     SelectObject( hdc, hbm_old );\r
2527 \r
2528     if( hPieceMask[index] != NULL ) {\r
2529         DeleteObject( hPieceMask[index] );\r
2530     }\r
2531 \r
2532     hPieceMask[index] = hbm;\r
2533 \r
2534     /* Face */\r
2535     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2536 \r
2537     SelectObject( hdc, hbm );\r
2538 \r
2539     {\r
2540         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2541         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2542         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2543 \r
2544         SelectObject( dc1, hPieceMask[index] );\r
2545         SelectObject( dc2, bm2 );\r
2546         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2547         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2548         \r
2549         /* \r
2550             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2551             the piece background and deletes (makes transparent) the rest.\r
2552             Thanks to that mask, we are free to paint the background with the greates\r
2553             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2554             We use this, to make gradients and give the pieces a "roundish" look.\r
2555         */\r
2556         SetPieceBackground( hdc, backColor, 2 );\r
2557         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2558 \r
2559         DeleteDC( dc2 );\r
2560         DeleteDC( dc1 );\r
2561         DeleteObject( bm2 );\r
2562     }\r
2563 \r
2564     SetTextColor( hdc, foreColor );\r
2565     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2566 \r
2567     SelectObject( hdc, hbm_old );\r
2568 \r
2569     if( hPieceFace[index] != NULL ) {\r
2570         DeleteObject( hPieceFace[index] );\r
2571     }\r
2572 \r
2573     hPieceFace[index] = hbm;\r
2574 }\r
2575 \r
2576 static int TranslatePieceToFontPiece( int piece )\r
2577 {\r
2578     switch( piece ) {\r
2579     case BlackPawn:\r
2580         return PM_BP;\r
2581     case BlackKnight:\r
2582         return PM_BN;\r
2583     case BlackBishop:\r
2584         return PM_BB;\r
2585     case BlackRook:\r
2586         return PM_BR;\r
2587     case BlackQueen:\r
2588         return PM_BQ;\r
2589     case BlackKing:\r
2590         return PM_BK;\r
2591     case WhitePawn:\r
2592         return PM_WP;\r
2593     case WhiteKnight:\r
2594         return PM_WN;\r
2595     case WhiteBishop:\r
2596         return PM_WB;\r
2597     case WhiteRook:\r
2598         return PM_WR;\r
2599     case WhiteQueen:\r
2600         return PM_WQ;\r
2601     case WhiteKing:\r
2602         return PM_WK;\r
2603     case BlackAngel:\r
2604         return PM_BA;\r
2605     case BlackMarshall:\r
2606         return PM_BC;\r
2607     case BlackFerz:\r
2608         return PM_BF;\r
2609     case BlackNightrider:\r
2610         return PM_BH;\r
2611     case BlackAlfil:\r
2612         return PM_BE;\r
2613     case BlackWazir:\r
2614         return PM_BW;\r
2615     case BlackUnicorn:\r
2616         return PM_BU;\r
2617     case BlackCannon:\r
2618         return PM_BO;\r
2619     case BlackGrasshopper:\r
2620         return PM_BG;\r
2621     case BlackMan:\r
2622         return PM_BM;\r
2623     case WhiteAngel:\r
2624         return PM_WA;\r
2625     case WhiteMarshall:\r
2626         return PM_WC;\r
2627     case WhiteFerz:\r
2628         return PM_WF;\r
2629     case WhiteNightrider:\r
2630         return PM_WH;\r
2631     case WhiteAlfil:\r
2632         return PM_WE;\r
2633     case WhiteWazir:\r
2634         return PM_WW;\r
2635     case WhiteUnicorn:\r
2636         return PM_WU;\r
2637     case WhiteCannon:\r
2638         return PM_WO;\r
2639     case WhiteGrasshopper:\r
2640         return PM_WG;\r
2641     case WhiteMan:\r
2642         return PM_WM;\r
2643     }\r
2644 \r
2645     return 0;\r
2646 }\r
2647 \r
2648 void CreatePiecesFromFont()\r
2649 {\r
2650     LOGFONT lf;\r
2651     HDC hdc_window = NULL;\r
2652     HDC hdc = NULL;\r
2653     HFONT hfont_old;\r
2654     int fontHeight;\r
2655     int i;\r
2656 \r
2657     if( fontBitmapSquareSize < 0 ) {\r
2658         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2659         return;\r
2660     }\r
2661 \r
2662     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2663         fontBitmapSquareSize = -1;\r
2664         return;\r
2665     }\r
2666 \r
2667     if( fontBitmapSquareSize != squareSize ) {\r
2668         hdc_window = GetDC( hwndMain );\r
2669         hdc = CreateCompatibleDC( hdc_window );\r
2670 \r
2671         if( hPieceFont != NULL ) {\r
2672             DeleteObject( hPieceFont );\r
2673         }\r
2674         else {\r
2675             for( i=0; i<12; i++ ) {\r
2676                 hPieceMask[i] = NULL;\r
2677                 hPieceFace[i] = NULL;\r
2678             }\r
2679         }\r
2680 \r
2681         fontHeight = 75;\r
2682 \r
2683         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2684             fontHeight = appData.fontPieceSize;\r
2685         }\r
2686 \r
2687         fontHeight = (fontHeight * squareSize) / 100;\r
2688 \r
2689         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2690         lf.lfWidth = 0;\r
2691         lf.lfEscapement = 0;\r
2692         lf.lfOrientation = 0;\r
2693         lf.lfWeight = FW_NORMAL;\r
2694         lf.lfItalic = 0;\r
2695         lf.lfUnderline = 0;\r
2696         lf.lfStrikeOut = 0;\r
2697         lf.lfCharSet = DEFAULT_CHARSET;\r
2698         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2699         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2700         lf.lfQuality = PROOF_QUALITY;\r
2701         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2702         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2703         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2704 \r
2705         hPieceFont = CreateFontIndirect( &lf );\r
2706 \r
2707         if( hPieceFont == NULL ) {\r
2708             fontBitmapSquareSize = -2;\r
2709         }\r
2710         else {\r
2711             /* Setup font-to-piece character table */\r
2712             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2713                 /* No (or wrong) global settings, try to detect the font */\r
2714                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2715                     /* Alpha */\r
2716                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2717                 }\r
2718                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2719                     /* DiagramTT* family */\r
2720                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2721                 }\r
2722                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2723                     /* Fairy symbols */\r
2724                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2725                 }\r
2726                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2727                     /* Good Companion (Some characters get warped as literal :-( */\r
2728                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2729                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2730                     SetCharTable(pieceToFontChar, s);\r
2731                 }\r
2732                 else {\r
2733                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2734                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2735                 }\r
2736             }\r
2737 \r
2738             /* Create bitmaps */\r
2739             hfont_old = SelectObject( hdc, hPieceFont );\r
2740 \r
2741             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2742             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2743             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2744             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2745             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2746             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2747             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2748             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2749             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2750             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2751             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2752             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2753 #ifdef FAIRY\r
2754             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2755             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2756             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2757             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2758             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2759             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2760             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2761             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2762             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2763             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2764             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2765             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2766             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2767             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2768             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2769             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2770             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2771             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2772             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2773             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2774 #endif\r
2775 \r
2776             SelectObject( hdc, hfont_old );\r
2777 \r
2778             fontBitmapSquareSize = squareSize;\r
2779         }\r
2780     }\r
2781 \r
2782     if( hdc != NULL ) {\r
2783         DeleteDC( hdc );\r
2784     }\r
2785 \r
2786     if( hdc_window != NULL ) {\r
2787         ReleaseDC( hwndMain, hdc_window );\r
2788     }\r
2789 }\r
2790 \r
2791 HBITMAP\r
2792 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2793 {\r
2794   char name[128];\r
2795 \r
2796   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2797   if (gameInfo.event &&\r
2798       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2799       strcmp(name, "k80s") == 0) {\r
2800     strcpy(name, "tim");\r
2801   }\r
2802   return LoadBitmap(hinst, name);\r
2803 }\r
2804 \r
2805 \r
2806 /* Insert a color into the program's logical palette\r
2807    structure.  This code assumes the given color is\r
2808    the result of the RGB or PALETTERGB macro, and it\r
2809    knows how those macros work (which is documented).\r
2810 */\r
2811 VOID\r
2812 InsertInPalette(COLORREF color)\r
2813 {\r
2814   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2815 \r
2816   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2817     DisplayFatalError("Too many colors", 0, 1);\r
2818     pLogPal->palNumEntries--;\r
2819     return;\r
2820   }\r
2821 \r
2822   pe->peFlags = (char) 0;\r
2823   pe->peRed = (char) (0xFF & color);\r
2824   pe->peGreen = (char) (0xFF & (color >> 8));\r
2825   pe->peBlue = (char) (0xFF & (color >> 16));\r
2826   return;\r
2827 }\r
2828 \r
2829 \r
2830 VOID\r
2831 InitDrawingColors()\r
2832 {\r
2833   if (pLogPal == NULL) {\r
2834     /* Allocate enough memory for a logical palette with\r
2835      * PALETTESIZE entries and set the size and version fields\r
2836      * of the logical palette structure.\r
2837      */\r
2838     pLogPal = (NPLOGPALETTE)\r
2839       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2840                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2841     pLogPal->palVersion    = 0x300;\r
2842   }\r
2843   pLogPal->palNumEntries = 0;\r
2844 \r
2845   InsertInPalette(lightSquareColor);\r
2846   InsertInPalette(darkSquareColor);\r
2847   InsertInPalette(whitePieceColor);\r
2848   InsertInPalette(blackPieceColor);\r
2849   InsertInPalette(highlightSquareColor);\r
2850   InsertInPalette(premoveHighlightColor);\r
2851 \r
2852   /*  create a logical color palette according the information\r
2853    *  in the LOGPALETTE structure.\r
2854    */\r
2855   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2856 \r
2857   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2858   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2859   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2860   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2861   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2862   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2863 \r
2864   /* [AS] Force rendering of the font-based pieces */\r
2865   if( fontBitmapSquareSize > 0 ) {\r
2866     fontBitmapSquareSize = 0;\r
2867   }\r
2868 }\r
2869 \r
2870 \r
2871 int\r
2872 BoardWidth(int boardSize, int n)\r
2873 { /* [HGM] argument n added to allow different width and height */\r
2874   int lineGap = sizeInfo[boardSize].lineGap;\r
2875 \r
2876   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2877       lineGap = appData.overrideLineGap;\r
2878   }\r
2879 \r
2880   return (n + 1) * lineGap +\r
2881           n * sizeInfo[boardSize].squareSize;\r
2882 }\r
2883 \r
2884 /* Respond to board resize by dragging edge */\r
2885 VOID\r
2886 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2887 {\r
2888   BoardSize newSize = NUM_SIZES - 1;\r
2889   static int recurse = 0;\r
2890   if (IsIconic(hwndMain)) return;\r
2891   if (recurse > 0) return;\r
2892   recurse++;\r
2893   while (newSize > 0 &&\r
2894          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2895           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2896     newSize--;\r
2897   } \r
2898   boardSize = newSize;\r
2899   InitDrawingSizes(boardSize, flags);\r
2900   recurse--;\r
2901 }\r
2902 \r
2903 \r
2904 \r
2905 VOID\r
2906 InitDrawingSizes(BoardSize boardSize, int flags)\r
2907 {\r
2908   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2909   ChessSquare piece;\r
2910   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2911   HDC hdc;\r
2912   SIZE clockSize, messageSize;\r
2913   HFONT oldFont;\r
2914   char buf[MSG_SIZ];\r
2915   char *str;\r
2916   HMENU hmenu = GetMenu(hwndMain);\r
2917   RECT crect, wrect;\r
2918   int offby;\r
2919   LOGBRUSH logbrush;\r
2920 \r
2921   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
2922   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2923 \r
2924   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2925   smallLayout = sizeInfo[boardSize].smallLayout;\r
2926   squareSize = sizeInfo[boardSize].squareSize;\r
2927   lineGap = sizeInfo[boardSize].lineGap;\r
2928   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2929 \r
2930   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2931       lineGap = appData.overrideLineGap;\r
2932   }\r
2933 \r
2934   if (tinyLayout != oldTinyLayout) {\r
2935     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2936     if (tinyLayout) {\r
2937       style &= ~WS_SYSMENU;\r
2938       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2939                  "&Minimize\tCtrl+F4");\r
2940     } else {\r
2941       style |= WS_SYSMENU;\r
2942       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2943     }\r
2944     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2945 \r
2946     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2947       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2948         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2949     }\r
2950     DrawMenuBar(hwndMain);\r
2951   }\r
2952 \r
2953   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2954   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2955 \r
2956   /* Get text area sizes */\r
2957   hdc = GetDC(hwndMain);\r
2958   if (appData.clockMode) {\r
2959     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2960   } else {\r
2961     sprintf(buf, "White");\r
2962   }\r
2963   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2964   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2965   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2966   str = "We only care about the height here";\r
2967   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2968   SelectObject(hdc, oldFont);\r
2969   ReleaseDC(hwndMain, hdc);\r
2970 \r
2971   /* Compute where everything goes */\r
2972   whiteRect.left = OUTER_MARGIN;\r
2973   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2974   whiteRect.top = OUTER_MARGIN;\r
2975   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2976 \r
2977   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2978   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2979   blackRect.top = whiteRect.top;\r
2980   blackRect.bottom = whiteRect.bottom;\r
2981 \r
2982   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2983   if (appData.showButtonBar) {\r
2984     messageRect.right = blackRect.right\r
2985       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2986   } else {\r
2987     messageRect.right = blackRect.right;\r
2988   }\r
2989   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2990   messageRect.bottom = messageRect.top + messageSize.cy;\r
2991 \r
2992   boardRect.left = whiteRect.left;\r
2993   boardRect.right = boardRect.left + boardWidth;\r
2994   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2995   boardRect.bottom = boardRect.top + boardHeight;\r
2996 \r
2997   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2998   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2999   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3000   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3001     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3002   GetWindowRect(hwndMain, &wrect);\r
3003   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3004                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3005   /* compensate if menu bar wrapped */\r
3006   GetClientRect(hwndMain, &crect);\r
3007   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3008   winHeight += offby;\r
3009   switch (flags) {\r
3010   case WMSZ_TOPLEFT:\r
3011     SetWindowPos(hwndMain, NULL, \r
3012                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3013                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3014     break;\r
3015 \r
3016   case WMSZ_TOPRIGHT:\r
3017   case WMSZ_TOP:\r
3018     SetWindowPos(hwndMain, NULL, \r
3019                  wrect.left, wrect.bottom - winHeight, \r
3020                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3021     break;\r
3022 \r
3023   case WMSZ_BOTTOMLEFT:\r
3024   case WMSZ_LEFT:\r
3025     SetWindowPos(hwndMain, NULL, \r
3026                  wrect.right - winWidth, wrect.top, \r
3027                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3028     break;\r
3029 \r
3030   case WMSZ_BOTTOMRIGHT:\r
3031   case WMSZ_BOTTOM:\r
3032   case WMSZ_RIGHT:\r
3033   default:\r
3034     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3035                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3036     break;\r
3037   }\r
3038 \r
3039   hwndPause = NULL;\r
3040   for (i = 0; i < N_BUTTONS; i++) {\r
3041     if (buttonDesc[i].hwnd != NULL) {\r
3042       DestroyWindow(buttonDesc[i].hwnd);\r
3043       buttonDesc[i].hwnd = NULL;\r
3044     }\r
3045     if (appData.showButtonBar) {\r
3046       buttonDesc[i].hwnd =\r
3047         CreateWindow("BUTTON", buttonDesc[i].label,\r
3048                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3049                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3050                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3051                      (HMENU) buttonDesc[i].id,\r
3052                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3053       if (tinyLayout) {\r
3054         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3055                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3056                     MAKELPARAM(FALSE, 0));\r
3057       }\r
3058       if (buttonDesc[i].id == IDM_Pause)\r
3059         hwndPause = buttonDesc[i].hwnd;\r
3060       buttonDesc[i].wndproc = (WNDPROC)\r
3061         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3062     }\r
3063   }\r
3064   if (gridPen != NULL) DeleteObject(gridPen);\r
3065   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3066   if (premovePen != NULL) DeleteObject(premovePen);\r
3067   if (lineGap != 0) {\r
3068     logbrush.lbStyle = BS_SOLID;\r
3069     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3070     gridPen =\r
3071       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3072                    lineGap, &logbrush, 0, NULL);\r
3073     logbrush.lbColor = highlightSquareColor;\r
3074     highlightPen =\r
3075       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3076                    lineGap, &logbrush, 0, NULL);\r
3077 \r
3078     logbrush.lbColor = premoveHighlightColor; \r
3079     premovePen =\r
3080       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3081                    lineGap, &logbrush, 0, NULL);\r
3082 \r
3083     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3084     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3085       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3086       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3087         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3088       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3089         BOARD_WIDTH * (squareSize + lineGap);\r
3090         lineGap / 2 + (i * (squareSize + lineGap));\r
3091       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3092     }\r
3093     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3094       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3095       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3096         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3097         lineGap / 2 + (i * (squareSize + lineGap));\r
3098       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3099         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3100       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3101     }\r
3102   }\r
3103 \r
3104   /* [HGM] Licensing requirement */\r
3105 #ifdef GOTHIC\r
3106   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3107 #endif\r
3108 #ifdef FALCON\r
3109   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3110 #endif\r
3111   GothicPopUp( "", VariantNormal);\r
3112 \r
3113 \r
3114 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3115   oldBoardSize = boardSize;\r
3116   oldTinyLayout = tinyLayout;\r
3117 \r
3118   /* Load piece bitmaps for this board size */\r
3119   for (i=0; i<=2; i++) {\r
3120     for (piece = WhitePawn;\r
3121          (int) piece < (int) BlackPawn;\r
3122          piece = (ChessSquare) ((int) piece + 1)) {\r
3123       if (pieceBitmap[i][piece] != NULL)\r
3124         DeleteObject(pieceBitmap[i][piece]);\r
3125     }\r
3126   }\r
3127 \r
3128   // Orthodox Chess pieces\r
3129   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3130   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3131   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3132   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3133   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3134   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3135   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3136   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3137   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3138   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3139   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3140   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3141   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3142   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3143   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3144   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3145     // in Shogi, Hijack the unused Queen for Lance\r
3146     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3147     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3148     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3149   } else {\r
3150     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3151     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3152     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3153   }\r
3154 \r
3155   if(squareSize <= 72 && squareSize >= 33) { \r
3156     /* A & C are available in most sizes now */\r
3157     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3158       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3159       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3160       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3161       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3162       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3163       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3164       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3165       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3166       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3167       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3168       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3169       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3170     } else { // Smirf-like\r
3171       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3172       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3173       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3174     }\r
3175     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3176       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3177       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3178       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3179     } else { // WinBoard standard\r
3180       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3181       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3182       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3183     }\r
3184   }\r
3185 \r
3186 \r
3187   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3188     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3189     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3190     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3191     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3192     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3193     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3194     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3195     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3196     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3197     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3198     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3199     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3200     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3201     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3202     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3203     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3204     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3205     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3206     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3207     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3208     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3209     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3210     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3211     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3212     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3213     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3214     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3215     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3216     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3217     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3218 \r
3219     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3220       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3221       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3222       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3223       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3224       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3225       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3226       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3227       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3228       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3229       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3230       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3231       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3232     } else {\r
3233       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3234       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3235       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3236       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3237       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3238       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3239       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3240       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3241       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3242       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3243       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3244       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3245     }\r
3246 \r
3247   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3248     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3249     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3250     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3251     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3252     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3253     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3254     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3255     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3256     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3257     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3258     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3259     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3260     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3261     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3262   }\r
3263 \r
3264 \r
3265   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3266   /* special Shogi support in this size */\r
3267   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3268       for (piece = WhitePawn;\r
3269            (int) piece < (int) BlackPawn;\r
3270            piece = (ChessSquare) ((int) piece + 1)) {\r
3271         if (pieceBitmap[i][piece] != NULL)\r
3272           DeleteObject(pieceBitmap[i][piece]);\r
3273       }\r
3274     }\r
3275   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3276   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3277   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3278   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3279   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3280   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3281   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3282   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3283   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3284   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3285   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3286   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3287   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3288   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3289   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3290   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3291   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3292   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3293   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3294   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3295   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3296   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3297   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3298   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3299   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3300   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3301   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3302   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3303   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3304   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3305   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3306   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3307   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3308   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3309   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3310   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3311   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3312   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3313   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3314   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3315   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3316   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3317   minorSize = 0;\r
3318   }\r
3319 }\r
3320 \r
3321 HBITMAP\r
3322 PieceBitmap(ChessSquare p, int kind)\r
3323 {\r
3324   if ((int) p >= (int) BlackPawn)\r
3325     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3326 \r
3327   return pieceBitmap[kind][(int) p];\r
3328 }\r
3329 \r
3330 /***************************************************************/\r
3331 \r
3332 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3333 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3334 /*\r
3335 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3336 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3337 */\r
3338 \r
3339 VOID\r
3340 SquareToPos(int row, int column, int * x, int * y)\r
3341 {\r
3342   if (flipView) {\r
3343     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3344     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3345   } else {\r
3346     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3347     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3348   }\r
3349 }\r
3350 \r
3351 VOID\r
3352 DrawCoordsOnDC(HDC hdc)\r
3353 {\r
3354   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
3355   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
3356   char str[2] = { NULLCHAR, NULLCHAR };\r
3357   int oldMode, oldAlign, x, y, start, i;\r
3358   HFONT oldFont;\r
3359   HBRUSH oldBrush;\r
3360 \r
3361   if (!appData.showCoords)\r
3362     return;\r
3363 \r
3364   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3365 \r
3366   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3367   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3368   oldAlign = GetTextAlign(hdc);\r
3369   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3370 \r
3371   y = boardRect.top + lineGap;\r
3372   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3373 \r
3374   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3375   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3376     str[0] = files[start + i];\r
3377     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3378     y += squareSize + lineGap;\r
3379   }\r
3380 \r
3381   start = flipView ? 12-BOARD_WIDTH : 12;\r
3382 \r
3383   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3384   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3385     str[0] = ranks[start + i];\r
3386     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3387     x += squareSize + lineGap;\r
3388   }    \r
3389 \r
3390   SelectObject(hdc, oldBrush);\r
3391   SetBkMode(hdc, oldMode);\r
3392   SetTextAlign(hdc, oldAlign);\r
3393   SelectObject(hdc, oldFont);\r
3394 }\r
3395 \r
3396 VOID\r
3397 DrawGridOnDC(HDC hdc)\r
3398 {\r
3399   HPEN oldPen;\r
3400  \r
3401   if (lineGap != 0) {\r
3402     oldPen = SelectObject(hdc, gridPen);\r
3403     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3404     SelectObject(hdc, oldPen);\r
3405   }\r
3406 }\r
3407 \r
3408 #define HIGHLIGHT_PEN 0\r
3409 #define PREMOVE_PEN   1\r
3410 \r
3411 VOID\r
3412 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3413 {\r
3414   int x1, y1;\r
3415   HPEN oldPen, hPen;\r
3416   if (lineGap == 0) return;\r
3417   if (flipView) {\r
3418     x1 = boardRect.left +\r
3419       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3420     y1 = boardRect.top +\r
3421       lineGap/2 + y * (squareSize + lineGap);\r
3422   } else {\r
3423     x1 = boardRect.left +\r
3424       lineGap/2 + x * (squareSize + lineGap);\r
3425     y1 = boardRect.top +\r
3426       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3427   }\r
3428   hPen = pen ? premovePen : highlightPen;\r
3429   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3430   MoveToEx(hdc, x1, y1, NULL);\r
3431   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3432   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3433   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3434   LineTo(hdc, x1, y1);\r
3435   SelectObject(hdc, oldPen);\r
3436 }\r
3437 \r
3438 VOID\r
3439 DrawHighlightsOnDC(HDC hdc)\r
3440 {\r
3441   int i;\r
3442   for (i=0; i<2; i++) {\r
3443     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3444       DrawHighlightOnDC(hdc, TRUE,\r
3445                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3446                         HIGHLIGHT_PEN);\r
3447   }\r
3448   for (i=0; i<2; i++) {\r
3449     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3450         premoveHighlightInfo.sq[i].y >= 0) {\r
3451         DrawHighlightOnDC(hdc, TRUE,\r
3452                           premoveHighlightInfo.sq[i].x, \r
3453                           premoveHighlightInfo.sq[i].y,\r
3454                           PREMOVE_PEN);\r
3455     }\r
3456   }\r
3457 }\r
3458 \r
3459 /* Note: sqcolor is used only in monoMode */\r
3460 /* Note that this code is largely duplicated in woptions.c,\r
3461    function DrawSampleSquare, so that needs to be updated too */\r
3462 VOID\r
3463 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3464 {\r
3465   HBITMAP oldBitmap;\r
3466   HBRUSH oldBrush;\r
3467   int tmpSize;\r
3468 \r
3469   if (appData.blindfold) return;\r
3470 \r
3471   /* [AS] Use font-based pieces if needed */\r
3472   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3473     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3474     CreatePiecesFromFont();\r
3475 \r
3476     if( fontBitmapSquareSize == squareSize ) {\r
3477         int index = TranslatePieceToFontPiece( piece );\r
3478 \r
3479         SelectObject( tmphdc, hPieceMask[ index ] );\r
3480 \r
3481         BitBlt( hdc,\r
3482             x, y,\r
3483             squareSize, squareSize,\r
3484             tmphdc,\r
3485             0, 0,\r
3486             SRCAND );\r
3487 \r
3488         SelectObject( tmphdc, hPieceFace[ index ] );\r
3489 \r
3490         BitBlt( hdc,\r
3491             x, y,\r
3492             squareSize, squareSize,\r
3493             tmphdc,\r
3494             0, 0,\r
3495             SRCPAINT );\r
3496 \r
3497         return;\r
3498     }\r
3499   }\r
3500 \r
3501   if (appData.monoMode) {\r
3502     SelectObject(tmphdc, PieceBitmap(piece, \r
3503       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3504     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3505            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3506   } else {\r
3507     tmpSize = squareSize;\r
3508     if(minorSize &&\r
3509         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3510          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3511       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3512       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3513       x += (squareSize - minorSize)>>1;\r
3514       y += squareSize - minorSize - 2;\r
3515       tmpSize = minorSize;\r
3516     }\r
3517     if (color || appData.allWhite ) {\r
3518       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3519       if( color )\r
3520               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3521       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3522       if(appData.upsideDown && color==flipView)\r
3523         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3524       else\r
3525         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3526 #if 0\r
3527       /* Use black piece color for outline of white pieces */\r
3528       /* Not sure this looks really good (though xboard does it).\r
3529          Maybe better to have another selectable color, default black */\r
3530       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3531       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3532       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3533 #else\r
3534       /* Use black for outline of white pieces */\r
3535       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3536       if(appData.upsideDown && color==flipView)\r
3537         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3538       else\r
3539         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3540 #endif\r
3541     } else {\r
3542 #if 0\r
3543       /* Use white piece color for details of black pieces */\r
3544       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3545          WHITE_PIECE ones aren't always the right shape. */\r
3546       /* Not sure this looks really good (though xboard does it).\r
3547          Maybe better to have another selectable color, default medium gray? */\r
3548       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3549       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3550       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3551       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3552       SelectObject(hdc, blackPieceBrush);\r
3553       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3554 #else\r
3555       /* Use square color for details of black pieces */\r
3556       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3557       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3558       if(appData.upsideDown && !flipView)\r
3559         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3560       else\r
3561         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3562 #endif\r
3563     }\r
3564     SelectObject(hdc, oldBrush);\r
3565     SelectObject(tmphdc, oldBitmap);\r
3566   }\r
3567 }\r
3568 \r
3569 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3570 int GetBackTextureMode( int algo )\r
3571 {\r
3572     int result = BACK_TEXTURE_MODE_DISABLED;\r
3573 \r
3574     switch( algo ) \r
3575     {\r
3576         case BACK_TEXTURE_MODE_PLAIN:\r
3577             result = 1; /* Always use identity map */\r
3578             break;\r
3579         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3580             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3581             break;\r
3582     }\r
3583 \r
3584     return result;\r
3585 }\r
3586 \r
3587 /* \r
3588     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3589     to handle redraws cleanly (as random numbers would always be different).\r
3590 */\r
3591 VOID RebuildTextureSquareInfo()\r
3592 {\r
3593     BITMAP bi;\r
3594     int lite_w = 0;\r
3595     int lite_h = 0;\r
3596     int dark_w = 0;\r
3597     int dark_h = 0;\r
3598     int row;\r
3599     int col;\r
3600 \r
3601     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3602 \r
3603     if( liteBackTexture != NULL ) {\r
3604         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3605             lite_w = bi.bmWidth;\r
3606             lite_h = bi.bmHeight;\r
3607         }\r
3608     }\r
3609 \r
3610     if( darkBackTexture != NULL ) {\r
3611         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3612             dark_w = bi.bmWidth;\r
3613             dark_h = bi.bmHeight;\r
3614         }\r
3615     }\r
3616 \r
3617     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3618         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3619             if( (col + row) & 1 ) {\r
3620                 /* Lite square */\r
3621                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3622                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3623                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3624                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3625                 }\r
3626             }\r
3627             else {\r
3628                 /* Dark square */\r
3629                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3630                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3631                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3632                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3633                 }\r
3634             }\r
3635         }\r
3636     }\r
3637 }\r
3638 \r
3639 /* [AS] Arrow highlighting support */\r
3640 \r
3641 static int A_WIDTH = 5; /* Width of arrow body */\r
3642 \r
3643 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3644 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3645 \r
3646 static double Sqr( double x )\r
3647 {\r
3648     return x*x;\r
3649 }\r
3650 \r
3651 static int Round( double x )\r
3652 {\r
3653     return (int) (x + 0.5);\r
3654 }\r
3655 \r
3656 /* Draw an arrow between two points using current settings */\r
3657 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3658 {\r
3659     POINT arrow[7];\r
3660     double dx, dy, j, k, x, y;\r
3661 \r
3662     if( d_x == s_x ) {\r
3663         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3664 \r
3665         arrow[0].x = s_x + A_WIDTH;\r
3666         arrow[0].y = s_y;\r
3667 \r
3668         arrow[1].x = s_x + A_WIDTH;\r
3669         arrow[1].y = d_y - h;\r
3670 \r
3671         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3672         arrow[2].y = d_y - h;\r
3673 \r
3674         arrow[3].x = d_x;\r
3675         arrow[3].y = d_y;\r
3676 \r
3677         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3678         arrow[4].y = d_y - h;\r
3679 \r
3680         arrow[5].x = s_x - A_WIDTH;\r
3681         arrow[5].y = d_y - h;\r
3682 \r
3683         arrow[6].x = s_x - A_WIDTH;\r
3684         arrow[6].y = s_y;\r
3685     }\r
3686     else if( d_y == s_y ) {\r
3687         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3688 \r
3689         arrow[0].x = s_x;\r
3690         arrow[0].y = s_y + A_WIDTH;\r
3691 \r
3692         arrow[1].x = d_x - w;\r
3693         arrow[1].y = s_y + A_WIDTH;\r
3694 \r
3695         arrow[2].x = d_x - w;\r
3696         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3697 \r
3698         arrow[3].x = d_x;\r
3699         arrow[3].y = d_y;\r
3700 \r
3701         arrow[4].x = d_x - w;\r
3702         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3703 \r
3704         arrow[5].x = d_x - w;\r
3705         arrow[5].y = s_y - A_WIDTH;\r
3706 \r
3707         arrow[6].x = s_x;\r
3708         arrow[6].y = s_y - A_WIDTH;\r
3709     }\r
3710     else {\r
3711         /* [AS] Needed a lot of paper for this! :-) */\r
3712         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3713         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3714   \r
3715         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3716 \r
3717         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3718 \r
3719         x = s_x;\r
3720         y = s_y;\r
3721 \r
3722         arrow[0].x = Round(x - j);\r
3723         arrow[0].y = Round(y + j*dx);\r
3724 \r
3725         arrow[1].x = Round(x + j);\r
3726         arrow[1].y = Round(y - j*dx);\r
3727 \r
3728         if( d_x > s_x ) {\r
3729             x = (double) d_x - k;\r
3730             y = (double) d_y - k*dy;\r
3731         }\r
3732         else {\r
3733             x = (double) d_x + k;\r
3734             y = (double) d_y + k*dy;\r
3735         }\r
3736 \r
3737         arrow[2].x = Round(x + j);\r
3738         arrow[2].y = Round(y - j*dx);\r
3739 \r
3740         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3741         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3742 \r
3743         arrow[4].x = d_x;\r
3744         arrow[4].y = d_y;\r
3745 \r
3746         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3747         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3748 \r
3749         arrow[6].x = Round(x - j);\r
3750         arrow[6].y = Round(y + j*dx);\r
3751     }\r
3752 \r
3753     Polygon( hdc, arrow, 7 );\r
3754 }\r
3755 \r
3756 /* [AS] Draw an arrow between two squares */\r
3757 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3758 {\r
3759     int s_x, s_y, d_x, d_y;\r
3760     HPEN hpen;\r
3761     HPEN holdpen;\r
3762     HBRUSH hbrush;\r
3763     HBRUSH holdbrush;\r
3764     LOGBRUSH stLB;\r
3765 \r
3766     if( s_col == d_col && s_row == d_row ) {\r
3767         return;\r
3768     }\r
3769 \r
3770     /* Get source and destination points */\r
3771     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3772     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3773 \r
3774     if( d_y > s_y ) {\r
3775         d_y += squareSize / 4;\r
3776     }\r
3777     else if( d_y < s_y ) {\r
3778         d_y += 3 * squareSize / 4;\r
3779     }\r
3780     else {\r
3781         d_y += squareSize / 2;\r
3782     }\r
3783 \r
3784     if( d_x > s_x ) {\r
3785         d_x += squareSize / 4;\r
3786     }\r
3787     else if( d_x < s_x ) {\r
3788         d_x += 3 * squareSize / 4;\r
3789     }\r
3790     else {\r
3791         d_x += squareSize / 2;\r
3792     }\r
3793 \r
3794     s_x += squareSize / 2;\r
3795     s_y += squareSize / 2;\r
3796 \r
3797     /* Adjust width */\r
3798     A_WIDTH = squareSize / 14;\r
3799 \r
3800     /* Draw */\r
3801     stLB.lbStyle = BS_SOLID;\r
3802     stLB.lbColor = appData.highlightArrowColor;\r
3803     stLB.lbHatch = 0;\r
3804 \r
3805     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3806     holdpen = SelectObject( hdc, hpen );\r
3807     hbrush = CreateBrushIndirect( &stLB );\r
3808     holdbrush = SelectObject( hdc, hbrush );\r
3809 \r
3810     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3811 \r
3812     SelectObject( hdc, holdpen );\r
3813     SelectObject( hdc, holdbrush );\r
3814     DeleteObject( hpen );\r
3815     DeleteObject( hbrush );\r
3816 }\r
3817 \r
3818 BOOL HasHighlightInfo()\r
3819 {\r
3820     BOOL result = FALSE;\r
3821 \r
3822     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3823         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3824     {\r
3825         result = TRUE;\r
3826     }\r
3827 \r
3828     return result;\r
3829 }\r
3830 \r
3831 BOOL IsDrawArrowEnabled()\r
3832 {\r
3833     BOOL result = FALSE;\r
3834 \r
3835     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3836         result = TRUE;\r
3837     }\r
3838 \r
3839     return result;\r
3840 }\r
3841 \r
3842 VOID DrawArrowHighlight( HDC hdc )\r
3843 {\r
3844     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3845         DrawArrowBetweenSquares( hdc,\r
3846             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3847             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3848     }\r
3849 }\r
3850 \r
3851 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3852 {\r
3853     HRGN result = NULL;\r
3854 \r
3855     if( HasHighlightInfo() ) {\r
3856         int x1, y1, x2, y2;\r
3857         int sx, sy, dx, dy;\r
3858 \r
3859         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3860         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3861 \r
3862         sx = MIN( x1, x2 );\r
3863         sy = MIN( y1, y2 );\r
3864         dx = MAX( x1, x2 ) + squareSize;\r
3865         dy = MAX( y1, y2 ) + squareSize;\r
3866 \r
3867         result = CreateRectRgn( sx, sy, dx, dy );\r
3868     }\r
3869 \r
3870     return result;\r
3871 }\r
3872 \r
3873 /*\r
3874     Warning: this function modifies the behavior of several other functions. \r
3875     \r
3876     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3877     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3878     repaint is scattered all over the place, which is not good for features such as\r
3879     "arrow highlighting" that require a full repaint of the board.\r
3880 \r
3881     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3882     user interaction, when speed is not so important) but especially to avoid errors\r
3883     in the displayed graphics.\r
3884 \r
3885     In such patched places, I always try refer to this function so there is a single\r
3886     place to maintain knowledge.\r
3887     \r
3888     To restore the original behavior, just return FALSE unconditionally.\r
3889 */\r
3890 BOOL IsFullRepaintPreferrable()\r
3891 {\r
3892     BOOL result = FALSE;\r
3893 \r
3894     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3895         /* Arrow may appear on the board */\r
3896         result = TRUE;\r
3897     }\r
3898 \r
3899     return result;\r
3900 }\r
3901 \r
3902 /* \r
3903     This function is called by DrawPosition to know whether a full repaint must\r
3904     be forced or not.\r
3905 \r
3906     Only DrawPosition may directly call this function, which makes use of \r
3907     some state information. Other function should call DrawPosition specifying \r
3908     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3909 */\r
3910 BOOL DrawPositionNeedsFullRepaint()\r
3911 {\r
3912     BOOL result = FALSE;\r
3913 \r
3914     /* \r
3915         Probably a slightly better policy would be to trigger a full repaint\r
3916         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3917         but animation is fast enough that it's difficult to notice.\r
3918     */\r
3919     if( animInfo.piece == EmptySquare ) {\r
3920         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3921             result = TRUE;\r
3922         }\r
3923     }\r
3924 \r
3925     return result;\r
3926 }\r
3927 \r
3928 VOID\r
3929 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3930 {\r
3931   int row, column, x, y, square_color, piece_color;\r
3932   ChessSquare piece;\r
3933   HBRUSH oldBrush;\r
3934   HDC texture_hdc = NULL;\r
3935 \r
3936   /* [AS] Initialize background textures if needed */\r
3937   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3938       if( backTextureSquareSize != squareSize ) {\r
3939           backTextureSquareSize = squareSize;\r
3940           RebuildTextureSquareInfo();\r
3941       }\r
3942 \r
3943       texture_hdc = CreateCompatibleDC( hdc );\r
3944   }\r
3945 \r
3946   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3947     for (column = 0; column < BOARD_WIDTH; column++) {\r
3948   \r
3949       SquareToPos(row, column, &x, &y);\r
3950 \r
3951       piece = board[row][column];\r
3952 \r
3953       square_color = ((column + row) % 2) == 1;\r
3954       if(!strcmp(appData.variant, "xiangqi") ) {\r
3955           square_color = !InPalace(row, column);\r
3956           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3957           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3958       }\r
3959       piece_color = (int) piece < (int) BlackPawn;\r
3960 \r
3961 \r
3962 #ifdef FAIRY\r
3963       /* [HGM] holdings file: light square or black */\r
3964       if(column == BOARD_LEFT-2) {\r
3965             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3966                 square_color = 1;\r
3967             else {\r
3968                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3969                 continue;\r
3970             }\r
3971       } else\r
3972       if(column == BOARD_RGHT + 1 ) {\r
3973             if( row < gameInfo.holdingsSize )\r
3974                 square_color = 1;\r
3975             else {\r
3976                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3977                 continue;\r
3978             }\r
3979       }\r
3980       if(column == BOARD_LEFT-1 ) /* left align */\r
3981             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3982       else if( column == BOARD_RGHT) /* right align */\r
3983             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3984       else\r
3985 #endif\r
3986       if (appData.monoMode) {\r
3987         if (piece == EmptySquare) {\r
3988           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3989                  square_color ? WHITENESS : BLACKNESS);\r
3990         } else {\r
3991           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3992         }\r
3993       } \r
3994       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3995           /* [AS] Draw the square using a texture bitmap */\r
3996           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3997 \r
3998           DrawTile( x, y, \r
3999               squareSize, squareSize, \r
4000               hdc, \r
4001               texture_hdc,\r
4002               backTextureSquareInfo[row][column].mode,\r
4003               backTextureSquareInfo[row][column].x,\r
4004               backTextureSquareInfo[row][column].y );\r
4005 \r
4006           SelectObject( texture_hdc, hbm );\r
4007 \r
4008           if (piece != EmptySquare) {\r
4009               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4010           }\r
4011       }\r
4012       else {\r
4013         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4014 \r
4015         oldBrush = SelectObject(hdc, brush );\r
4016         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4017         SelectObject(hdc, oldBrush);\r
4018         if (piece != EmptySquare)\r
4019           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4020       }\r
4021     }\r
4022   }\r
4023 \r
4024   if( texture_hdc != NULL ) {\r
4025     DeleteDC( texture_hdc );\r
4026   }\r
4027 }\r
4028 \r
4029 #define MAX_CLIPS 200   /* more than enough */\r
4030 \r
4031 VOID\r
4032 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4033 {\r
4034   static Board lastReq, lastDrawn;\r
4035   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4036   static int lastDrawnFlipView = 0;\r
4037   static int lastReqValid = 0, lastDrawnValid = 0;\r
4038   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4039   HDC tmphdc;\r
4040   HDC hdcmem;\r
4041   HBITMAP bufferBitmap;\r
4042   HBITMAP oldBitmap;\r
4043   RECT Rect;\r
4044   HRGN clips[MAX_CLIPS];\r
4045   ChessSquare dragged_piece = EmptySquare;\r
4046 \r
4047   /* I'm undecided on this - this function figures out whether a full\r
4048    * repaint is necessary on its own, so there's no real reason to have the\r
4049    * caller tell it that.  I think this can safely be set to FALSE - but\r
4050    * if we trust the callers not to request full repaints unnessesarily, then\r
4051    * we could skip some clipping work.  In other words, only request a full\r
4052    * redraw when the majority of pieces have changed positions (ie. flip, \r
4053    * gamestart and similar)  --Hawk\r
4054    */\r
4055   Boolean fullrepaint = repaint;\r
4056 \r
4057   if( DrawPositionNeedsFullRepaint() ) {\r
4058       fullrepaint = TRUE;\r
4059   }\r
4060 \r
4061 #if 0\r
4062   if( fullrepaint ) {\r
4063       static int repaint_count = 0;\r
4064       char buf[128];\r
4065 \r
4066       repaint_count++;\r
4067       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4068       OutputDebugString( buf );\r
4069   }\r
4070 #endif\r
4071 \r
4072   if (board == NULL) {\r
4073     if (!lastReqValid) {\r
4074       return;\r
4075     }\r
4076     board = lastReq;\r
4077   } else {\r
4078     CopyBoard(lastReq, board);\r
4079     lastReqValid = 1;\r
4080   }\r
4081 \r
4082   if (doingSizing) {\r
4083     return;\r
4084   }\r
4085 \r
4086   if (IsIconic(hwndMain)) {\r
4087     return;\r
4088   }\r
4089 \r
4090   if (hdc == NULL) {\r
4091     hdc = GetDC(hwndMain);\r
4092     if (!appData.monoMode) {\r
4093       SelectPalette(hdc, hPal, FALSE);\r
4094       RealizePalette(hdc);\r
4095     }\r
4096     releaseDC = TRUE;\r
4097   } else {\r
4098     releaseDC = FALSE;\r
4099   }\r
4100 \r
4101 #if 0\r
4102   fprintf(debugFP, "*******************************\n"\r
4103                    "repaint = %s\n"\r
4104                    "dragInfo.from (%d,%d)\n"\r
4105                    "dragInfo.start (%d,%d)\n"\r
4106                    "dragInfo.pos (%d,%d)\n"\r
4107                    "dragInfo.lastpos (%d,%d)\n", \r
4108                     repaint ? "TRUE" : "FALSE",\r
4109                     dragInfo.from.x, dragInfo.from.y, \r
4110                     dragInfo.start.x, dragInfo.start.y,\r
4111                     dragInfo.pos.x, dragInfo.pos.y,\r
4112                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4113   fprintf(debugFP, "prev:  ");\r
4114   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4115     for (column = 0; column < BOARD_WIDTH; column++) {\r
4116       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4117     }\r
4118   }\r
4119   fprintf(debugFP, "\n");\r
4120   fprintf(debugFP, "board: ");\r
4121   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4122     for (column = 0; column < BOARD_WIDTH; column++) {\r
4123       fprintf(debugFP, "%d ", board[row][column]);\r
4124     }\r
4125   }\r
4126   fprintf(debugFP, "\n");\r
4127   fflush(debugFP);\r
4128 #endif\r
4129 \r
4130   /* Create some work-DCs */\r
4131   hdcmem = CreateCompatibleDC(hdc);\r
4132   tmphdc = CreateCompatibleDC(hdc);\r
4133 \r
4134   /* If dragging is in progress, we temporarely remove the piece */\r
4135   /* [HGM] or temporarily decrease count if stacked              */\r
4136   /*       !! Moved to before board compare !!                   */\r
4137   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4138     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4139     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4140             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4141         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4142     } else \r
4143     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4144             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4145         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4146     } else \r
4147         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4148   }\r
4149 \r
4150   /* Figure out which squares need updating by comparing the \r
4151    * newest board with the last drawn board and checking if\r
4152    * flipping has changed.\r
4153    */\r
4154   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4155     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4156       for (column = 0; column < BOARD_WIDTH; column++) {\r
4157         if (lastDrawn[row][column] != board[row][column]) {\r
4158           SquareToPos(row, column, &x, &y);\r
4159           clips[num_clips++] =\r
4160             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4161         }\r
4162       }\r
4163     }\r
4164     for (i=0; i<2; i++) {\r
4165       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4166           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4167         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4168             lastDrawnHighlight.sq[i].y >= 0) {\r
4169           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4170                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4171           clips[num_clips++] =\r
4172             CreateRectRgn(x - lineGap, y - lineGap, \r
4173                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4174         }\r
4175         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4176           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4177           clips[num_clips++] =\r
4178             CreateRectRgn(x - lineGap, y - lineGap, \r
4179                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4180         }\r
4181       }\r
4182     }\r
4183     for (i=0; i<2; i++) {\r
4184       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4185           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4186         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4187             lastDrawnPremove.sq[i].y >= 0) {\r
4188           SquareToPos(lastDrawnPremove.sq[i].y,\r
4189                       lastDrawnPremove.sq[i].x, &x, &y);\r
4190           clips[num_clips++] =\r
4191             CreateRectRgn(x - lineGap, y - lineGap, \r
4192                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4193         }\r
4194         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4195             premoveHighlightInfo.sq[i].y >= 0) {\r
4196           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4197                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4198           clips[num_clips++] =\r
4199             CreateRectRgn(x - lineGap, y - lineGap, \r
4200                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4201         }\r
4202       }\r
4203     }\r
4204   } else {\r
4205     fullrepaint = TRUE;\r
4206   }\r
4207 \r
4208   /* Create a buffer bitmap - this is the actual bitmap\r
4209    * being written to.  When all the work is done, we can\r
4210    * copy it to the real DC (the screen).  This avoids\r
4211    * the problems with flickering.\r
4212    */\r
4213   GetClientRect(hwndMain, &Rect);\r
4214   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4215                                         Rect.bottom-Rect.top+1);\r
4216   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4217   if (!appData.monoMode) {\r
4218     SelectPalette(hdcmem, hPal, FALSE);\r
4219   }\r
4220 \r
4221   /* Create clips for dragging */\r
4222   if (!fullrepaint) {\r
4223     if (dragInfo.from.x >= 0) {\r
4224       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4225       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4226     }\r
4227     if (dragInfo.start.x >= 0) {\r
4228       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4229       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4230     }\r
4231     if (dragInfo.pos.x >= 0) {\r
4232       x = dragInfo.pos.x - squareSize / 2;\r
4233       y = dragInfo.pos.y - squareSize / 2;\r
4234       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4235     }\r
4236     if (dragInfo.lastpos.x >= 0) {\r
4237       x = dragInfo.lastpos.x - squareSize / 2;\r
4238       y = dragInfo.lastpos.y - squareSize / 2;\r
4239       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4240     }\r
4241   }\r
4242 \r
4243   /* Are we animating a move?  \r
4244    * If so, \r
4245    *   - remove the piece from the board (temporarely)\r
4246    *   - calculate the clipping region\r
4247    */\r
4248   if (!fullrepaint) {\r
4249     if (animInfo.piece != EmptySquare) {\r
4250       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4251       x = boardRect.left + animInfo.lastpos.x;\r
4252       y = boardRect.top + animInfo.lastpos.y;\r
4253       x2 = boardRect.left + animInfo.pos.x;\r
4254       y2 = boardRect.top + animInfo.pos.y;\r
4255       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4256       /* Slight kludge.  The real problem is that after AnimateMove is\r
4257          done, the position on the screen does not match lastDrawn.\r
4258          This currently causes trouble only on e.p. captures in\r
4259          atomic, where the piece moves to an empty square and then\r
4260          explodes.  The old and new positions both had an empty square\r
4261          at the destination, but animation has drawn a piece there and\r
4262          we have to remember to erase it. */\r
4263       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4264     }\r
4265   }\r
4266 \r
4267   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4268   if (num_clips == 0)\r
4269     fullrepaint = TRUE;\r
4270 \r
4271   /* Set clipping on the memory DC */\r
4272   if (!fullrepaint) {\r
4273     SelectClipRgn(hdcmem, clips[0]);\r
4274     for (x = 1; x < num_clips; x++) {\r
4275       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4276         abort();  // this should never ever happen!\r
4277     }\r
4278   }\r
4279 \r
4280   /* Do all the drawing to the memory DC */\r
4281   DrawGridOnDC(hdcmem);\r
4282   DrawHighlightsOnDC(hdcmem);\r
4283   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4284 \r
4285   if( appData.highlightMoveWithArrow ) {\r
4286     DrawArrowHighlight(hdcmem);\r
4287   }\r
4288 \r
4289   DrawCoordsOnDC(hdcmem);\r
4290 \r
4291   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4292                  /* to make sure lastDrawn contains what is actually drawn */\r
4293 \r
4294   /* Put the dragged piece back into place and draw it (out of place!) */\r
4295     if (dragged_piece != EmptySquare) {\r
4296     /* [HGM] or restack */\r
4297     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4298                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4299     else\r
4300     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4301                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4302     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4303     x = dragInfo.pos.x - squareSize / 2;\r
4304     y = dragInfo.pos.y - squareSize / 2;\r
4305     DrawPieceOnDC(hdcmem, dragged_piece,\r
4306                   ((int) dragged_piece < (int) BlackPawn), \r
4307                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4308   }   \r
4309   \r
4310   /* Put the animated piece back into place and draw it */\r
4311   if (animInfo.piece != EmptySquare) {\r
4312     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4313     x = boardRect.left + animInfo.pos.x;\r
4314     y = boardRect.top + animInfo.pos.y;\r
4315     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4316                   ((int) animInfo.piece < (int) BlackPawn),\r
4317                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4318   }\r
4319 \r
4320   /* Release the bufferBitmap by selecting in the old bitmap \r
4321    * and delete the memory DC\r
4322    */\r
4323   SelectObject(hdcmem, oldBitmap);\r
4324   DeleteDC(hdcmem);\r
4325 \r
4326   /* Set clipping on the target DC */\r
4327   if (!fullrepaint) {\r
4328     SelectClipRgn(hdc, clips[0]);\r
4329     for (x = 1; x < num_clips; x++) {\r
4330       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4331         abort();   // this should never ever happen!\r
4332     } \r
4333   }\r
4334 \r
4335   /* Copy the new bitmap onto the screen in one go.\r
4336    * This way we avoid any flickering\r
4337    */\r
4338   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4339   BitBlt(hdc, boardRect.left, boardRect.top,\r
4340          boardRect.right - boardRect.left,\r
4341          boardRect.bottom - boardRect.top,\r
4342          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4343   SelectObject(tmphdc, oldBitmap);\r
4344 \r
4345   /* Massive cleanup */\r
4346   for (x = 0; x < num_clips; x++)\r
4347     DeleteObject(clips[x]);\r
4348 \r
4349   DeleteDC(tmphdc);\r
4350   DeleteObject(bufferBitmap);\r
4351 \r
4352   if (releaseDC) \r
4353     ReleaseDC(hwndMain, hdc);\r
4354   \r
4355   if (lastDrawnFlipView != flipView) {\r
4356     if (flipView)\r
4357       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4358     else\r
4359       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4360   }\r
4361 \r
4362 /*  CopyBoard(lastDrawn, board);*/\r
4363   lastDrawnHighlight = highlightInfo;\r
4364   lastDrawnPremove   = premoveHighlightInfo;\r
4365   lastDrawnFlipView = flipView;\r
4366   lastDrawnValid = 1;\r
4367 }\r
4368 \r
4369 \r
4370 /*---------------------------------------------------------------------------*\\r
4371 | CLIENT PAINT PROCEDURE\r
4372 |   This is the main event-handler for the WM_PAINT message.\r
4373 |\r
4374 \*---------------------------------------------------------------------------*/\r
4375 VOID\r
4376 PaintProc(HWND hwnd)\r
4377 {\r
4378   HDC         hdc;\r
4379   PAINTSTRUCT ps;\r
4380   HFONT       oldFont;\r
4381 \r
4382   if(hdc = BeginPaint(hwnd, &ps)) {\r
4383     if (IsIconic(hwnd)) {\r
4384       DrawIcon(hdc, 2, 2, iconCurrent);\r
4385     } else {\r
4386       if (!appData.monoMode) {\r
4387         SelectPalette(hdc, hPal, FALSE);\r
4388         RealizePalette(hdc);\r
4389       }\r
4390       HDCDrawPosition(hdc, 1, NULL);\r
4391       oldFont =\r
4392         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4393       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4394                  ETO_CLIPPED|ETO_OPAQUE,\r
4395                  &messageRect, messageText, strlen(messageText), NULL);\r
4396       SelectObject(hdc, oldFont);\r
4397       DisplayBothClocks();\r
4398     }\r
4399     EndPaint(hwnd,&ps);\r
4400   }\r
4401 \r
4402   return;\r
4403 }\r
4404 \r
4405 \r
4406 /*\r
4407  * If the user selects on a border boundary, return -1; if off the board,\r
4408  *   return -2.  Otherwise map the event coordinate to the square.\r
4409  * The offset boardRect.left or boardRect.top must already have been\r
4410  *   subtracted from x.\r
4411  */\r
4412 int\r
4413 EventToSquare(int x)\r
4414 {\r
4415   if (x <= 0)\r
4416     return -2;\r
4417   if (x < lineGap)\r
4418     return -1;\r
4419   x -= lineGap;\r
4420   if ((x % (squareSize + lineGap)) >= squareSize)\r
4421     return -1;\r
4422   x /= (squareSize + lineGap);\r
4423   if (x >= BOARD_SIZE)\r
4424     return -2;\r
4425   return x;\r
4426 }\r
4427 \r
4428 typedef struct {\r
4429   char piece;\r
4430   int command;\r
4431   char* name;\r
4432 } DropEnable;\r
4433 \r
4434 DropEnable dropEnables[] = {\r
4435   { 'P', DP_Pawn, "Pawn" },\r
4436   { 'N', DP_Knight, "Knight" },\r
4437   { 'B', DP_Bishop, "Bishop" },\r
4438   { 'R', DP_Rook, "Rook" },\r
4439   { 'Q', DP_Queen, "Queen" },\r
4440 };\r
4441 \r
4442 VOID\r
4443 SetupDropMenu(HMENU hmenu)\r
4444 {\r
4445   int i, count, enable;\r
4446   char *p;\r
4447   extern char white_holding[], black_holding[];\r
4448   char item[MSG_SIZ];\r
4449 \r
4450   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4451     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4452                dropEnables[i].piece);\r
4453     count = 0;\r
4454     while (p && *p++ == dropEnables[i].piece) count++;\r
4455     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4456     enable = count > 0 || !appData.testLegality\r
4457       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4458                       && !appData.icsActive);\r
4459     ModifyMenu(hmenu, dropEnables[i].command,\r
4460                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4461                dropEnables[i].command, item);\r
4462   }\r
4463 }\r
4464 \r
4465 static int fromX = -1, fromY = -1, toX, toY;\r
4466 \r
4467 /* Event handler for mouse messages */\r
4468 VOID\r
4469 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4470 {\r
4471   int x, y;\r
4472   POINT pt;\r
4473   static int recursive = 0;\r
4474   HMENU hmenu;\r
4475   BOOLEAN needsRedraw = FALSE;\r
4476   BOOLEAN saveAnimate;\r
4477   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4478   static BOOLEAN sameAgain = FALSE;\r
4479   ChessMove moveType;\r
4480 \r
4481   if (recursive) {\r
4482     if (message == WM_MBUTTONUP) {\r
4483       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4484          to the middle button: we simulate pressing the left button too!\r
4485          */\r
4486       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4487       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4488     }\r
4489     return;\r
4490   }\r
4491   recursive++;\r
4492   \r
4493   pt.x = LOWORD(lParam);\r
4494   pt.y = HIWORD(lParam);\r
4495   x = EventToSquare(pt.x - boardRect.left);\r
4496   y = EventToSquare(pt.y - boardRect.top);\r
4497   if (!flipView && y >= 0) {\r
4498     y = BOARD_HEIGHT - 1 - y;\r
4499   }\r
4500   if (flipView && x >= 0) {\r
4501     x = BOARD_WIDTH - 1 - x;\r
4502   }\r
4503 \r
4504   switch (message) {\r
4505   case WM_LBUTTONDOWN:\r
4506     ErrorPopDown();\r
4507     sameAgain = FALSE;\r
4508     if (y == -2) {\r
4509       /* Downclick vertically off board; check if on clock */\r
4510       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4511         if (gameMode == EditPosition) {\r
4512           SetWhiteToPlayEvent();\r
4513         } else if (gameMode == IcsPlayingBlack ||\r
4514                    gameMode == MachinePlaysWhite) {\r
4515           CallFlagEvent();\r
4516         } else if (gameMode == EditGame) {\r
4517           AdjustClock(flipClock, -1);\r
4518         }\r
4519       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4520         if (gameMode == EditPosition) {\r
4521           SetBlackToPlayEvent();\r
4522         } else if (gameMode == IcsPlayingWhite ||\r
4523                    gameMode == MachinePlaysBlack) {\r
4524           CallFlagEvent();\r
4525         } else if (gameMode == EditGame) {\r
4526           AdjustClock(!flipClock, -1);\r
4527         }\r
4528       }\r
4529       if (!appData.highlightLastMove) {\r
4530         ClearHighlights();\r
4531         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4532       }\r
4533       fromX = fromY = -1;\r
4534       dragInfo.start.x = dragInfo.start.y = -1;\r
4535       dragInfo.from = dragInfo.start;\r
4536       break;\r
4537     } else if (x < 0 || y < 0\r
4538       /* [HGM] block clicks between board and holdings */\r
4539               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4540               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4541               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4542         /* EditPosition, empty square, or different color piece;\r
4543            click-click move is possible */\r
4544                                ) {\r
4545       break;\r
4546     } else if (fromX == x && fromY == y) {\r
4547       /* Downclick on same square again */\r
4548       ClearHighlights();\r
4549       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4550       sameAgain = TRUE;  \r
4551     } else if (fromX != -1 &&\r
4552                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4553                                                                         ) {\r
4554       /* Downclick on different square. */\r
4555       /* [HGM] if on holdings file, should count as new first click ! */\r
4556       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4557         toX = x;\r
4558         toY = y;\r
4559         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4560            to make sure move is legal before showing promotion popup */\r
4561         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4562         if(moveType != ImpossibleMove) {\r
4563           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4564           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4565              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4566               appData.alwaysPromoteToQueen) {\r
4567                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4568                   if (!appData.highlightLastMove) {\r
4569                       ClearHighlights();\r
4570                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4571                   }\r
4572           } else\r
4573           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4574                   SetHighlights(fromX, fromY, toX, toY);\r
4575                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4576                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4577                      If promotion to Q is legal, all are legal! */\r
4578                   PromotionPopup(hwnd);\r
4579           } else {       /* not a promotion */\r
4580              if (appData.animate || appData.highlightLastMove) {\r
4581                  SetHighlights(fromX, fromY, toX, toY);\r
4582              } else {\r
4583                  ClearHighlights();\r
4584              }\r
4585              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4586              if (appData.animate && !appData.highlightLastMove) {\r
4587                   ClearHighlights();\r
4588                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4589              }\r
4590           }\r
4591           break;\r
4592         }\r
4593         if (gotPremove) {\r
4594             /* [HGM] it seemed that braces were missing here */\r
4595             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4596             fromX = fromY = -1;\r
4597             break;\r
4598         }\r
4599       }\r
4600       ClearHighlights();\r
4601       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4602     }\r
4603     /* First downclick, or restart on a square with same color piece */\r
4604     if (!frozen && OKToStartUserMove(x, y)) {\r
4605       fromX = x;\r
4606       fromY = y;\r
4607       dragInfo.lastpos = pt;\r
4608       dragInfo.from.x = fromX;\r
4609       dragInfo.from.y = fromY;\r
4610       dragInfo.start = dragInfo.from;\r
4611       SetCapture(hwndMain);\r
4612     } else {\r
4613       fromX = fromY = -1;\r
4614       dragInfo.start.x = dragInfo.start.y = -1;\r
4615       dragInfo.from = dragInfo.start;\r
4616       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4617     }\r
4618     break;\r
4619 \r
4620   case WM_LBUTTONUP:\r
4621     ReleaseCapture();\r
4622     if (fromX == -1) break;\r
4623     if (x == fromX && y == fromY) {\r
4624       dragInfo.from.x = dragInfo.from.y = -1;\r
4625       /* Upclick on same square */\r
4626       if (sameAgain) {\r
4627         /* Clicked same square twice: abort click-click move */\r
4628         fromX = fromY = -1;\r
4629         gotPremove = 0;\r
4630         ClearPremoveHighlights();\r
4631       } else {\r
4632         /* First square clicked: start click-click move */\r
4633         SetHighlights(fromX, fromY, -1, -1);\r
4634       }\r
4635       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4636     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4637       /* Errant click; ignore */\r
4638       break;\r
4639     } else {\r
4640       /* Finish drag move. */\r
4641     if (appData.debugMode) {\r
4642         fprintf(debugFP, "release\n");\r
4643     }\r
4644       dragInfo.from.x = dragInfo.from.y = -1;\r
4645       toX = x;\r
4646       toY = y;\r
4647       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4648       appData.animate = appData.animate && !appData.animateDragging;\r
4649       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4650       if(moveType != ImpossibleMove) {\r
4651           /* [HGM] use move type to determine if move is promotion.\r
4652              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4653           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4654              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4655               appData.alwaysPromoteToQueen) \r
4656                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4657           else \r
4658           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4659                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4660                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4661         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4662       }\r
4663       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4664       appData.animate = saveAnimate;\r
4665       fromX = fromY = -1;\r
4666       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4667         ClearHighlights();\r
4668       }\r
4669       if (appData.animate || appData.animateDragging ||\r
4670           appData.highlightDragging || gotPremove) {\r
4671         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4672       }\r
4673     }\r
4674     dragInfo.start.x = dragInfo.start.y = -1; \r
4675     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4676     break;\r
4677 \r
4678   case WM_MOUSEMOVE:\r
4679     if ((appData.animateDragging || appData.highlightDragging)\r
4680         && (wParam & MK_LBUTTON)\r
4681         && dragInfo.from.x >= 0) \r
4682     {\r
4683       BOOL full_repaint = FALSE;\r
4684 \r
4685       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4686       if (appData.animateDragging) {\r
4687         dragInfo.pos = pt;\r
4688       }\r
4689       if (appData.highlightDragging) {\r
4690         SetHighlights(fromX, fromY, x, y);\r
4691         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4692             full_repaint = TRUE;\r
4693         }\r
4694       }\r
4695       \r
4696       DrawPosition( full_repaint, NULL);\r
4697       \r
4698       dragInfo.lastpos = dragInfo.pos;\r
4699     }\r
4700     break;\r
4701 \r
4702   case WM_MBUTTONDOWN:\r
4703   case WM_RBUTTONDOWN:\r
4704     ErrorPopDown();\r
4705     ReleaseCapture();\r
4706     fromX = fromY = -1;\r
4707     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4708     dragInfo.start.x = dragInfo.start.y = -1;\r
4709     dragInfo.from = dragInfo.start;\r
4710     dragInfo.lastpos = dragInfo.pos;\r
4711     if (appData.highlightDragging) {\r
4712       ClearHighlights();\r
4713     }\r
4714     if(y == -2) {\r
4715       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4716       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4717           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4718       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4719           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4720       }\r
4721     }\r
4722     DrawPosition(TRUE, NULL);\r
4723 \r
4724     switch (gameMode) {\r
4725     case EditPosition:\r
4726     case IcsExamining:\r
4727       if (x < 0 || y < 0) break;\r
4728       fromX = x;\r
4729       fromY = y;\r
4730       if (message == WM_MBUTTONDOWN) {\r
4731         buttonCount = 3;  /* even if system didn't think so */\r
4732         if (wParam & MK_SHIFT) \r
4733           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4734         else\r
4735           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4736       } else { /* message == WM_RBUTTONDOWN */\r
4737 #if 0\r
4738         if (buttonCount == 3) {\r
4739           if (wParam & MK_SHIFT) \r
4740             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4741           else\r
4742             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4743         } else {\r
4744           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4745         }\r
4746 #else\r
4747         /* Just have one menu, on the right button.  Windows users don't\r
4748            think to try the middle one, and sometimes other software steals\r
4749            it, or it doesn't really exist. */\r
4750         if(gameInfo.variant != VariantShogi)\r
4751             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4752         else\r
4753             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4754 #endif\r
4755       }\r
4756       break;\r
4757     case IcsPlayingWhite:\r
4758     case IcsPlayingBlack:\r
4759     case EditGame:\r
4760     case MachinePlaysWhite:\r
4761     case MachinePlaysBlack:\r
4762       if (appData.testLegality &&\r
4763           gameInfo.variant != VariantBughouse &&\r
4764           gameInfo.variant != VariantCrazyhouse) break;\r
4765       if (x < 0 || y < 0) break;\r
4766       fromX = x;\r
4767       fromY = y;\r
4768       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4769       SetupDropMenu(hmenu);\r
4770       MenuPopup(hwnd, pt, hmenu, -1);\r
4771       break;\r
4772     default:\r
4773       break;\r
4774     }\r
4775     break;\r
4776   }\r
4777 \r
4778   recursive--;\r
4779 }\r
4780 \r
4781 /* Preprocess messages for buttons in main window */\r
4782 LRESULT CALLBACK\r
4783 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4784 {\r
4785   int id = GetWindowLong(hwnd, GWL_ID);\r
4786   int i, dir;\r
4787 \r
4788   for (i=0; i<N_BUTTONS; i++) {\r
4789     if (buttonDesc[i].id == id) break;\r
4790   }\r
4791   if (i == N_BUTTONS) return 0;\r
4792   switch (message) {\r
4793   case WM_KEYDOWN:\r
4794     switch (wParam) {\r
4795     case VK_LEFT:\r
4796     case VK_RIGHT:\r
4797       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4798       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4799       return TRUE;\r
4800     }\r
4801     break;\r
4802   case WM_CHAR:\r
4803     switch (wParam) {\r
4804     case '\r':\r
4805       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4806       return TRUE;\r
4807     case '\t':\r
4808       if (appData.icsActive) {\r
4809         if (GetKeyState(VK_SHIFT) < 0) {\r
4810           /* shifted */\r
4811           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4812           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4813           SetFocus(h);\r
4814         } else {\r
4815           /* unshifted */\r
4816           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4817           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4818           SetFocus(h);\r
4819         }\r
4820         return TRUE;\r
4821       }\r
4822       break;\r
4823     default:\r
4824       if (appData.icsActive) {\r
4825         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4826         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4827         SetFocus(h);\r
4828         SendMessage(h, WM_CHAR, wParam, lParam);\r
4829         return TRUE;\r
4830       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4831         PopUpMoveDialog((char)wParam);\r
4832       }\r
4833       break;\r
4834     }\r
4835     break;\r
4836   }\r
4837   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4838 }\r
4839 \r
4840 /* Process messages for Promotion dialog box */\r
4841 LRESULT CALLBACK\r
4842 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4843 {\r
4844   char promoChar;\r
4845 \r
4846   switch (message) {\r
4847   case WM_INITDIALOG: /* message: initialize dialog box */\r
4848     /* Center the dialog over the application window */\r
4849     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4850     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4851       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4852        gameInfo.variant == VariantGiveaway) ?\r
4853                SW_SHOW : SW_HIDE);\r
4854     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4855     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4856        (PieceToChar(WhiteCardinal) >= 'A' &&\r
4857         PieceToChar(WhiteCardinal) != '~' ||\r
4858         PieceToChar(BlackCardinal) >= 'A' &&\r
4859         PieceToChar(BlackCardinal) != '~'   ) ?\r
4860                SW_SHOW : SW_HIDE);\r
4861     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4862        (PieceToChar(WhiteMarshall) >= 'A' &&\r
4863         PieceToChar(WhiteMarshall) != '~' ||\r
4864         PieceToChar(BlackMarshall) >= 'A' &&\r
4865         PieceToChar(BlackMarshall) != '~'   ) ?\r
4866                SW_SHOW : SW_HIDE);\r
4867     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4868     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4869        gameInfo.variant != VariantShogi ?\r
4870                SW_SHOW : SW_HIDE);\r
4871     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4872        gameInfo.variant != VariantShogi ?\r
4873                SW_SHOW : SW_HIDE);\r
4874     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4875        gameInfo.variant == VariantShogi ?\r
4876                SW_SHOW : SW_HIDE);\r
4877     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4878        gameInfo.variant == VariantShogi ?\r
4879                SW_SHOW : SW_HIDE);\r
4880     return TRUE;\r
4881 \r
4882   case WM_COMMAND: /* message: received a command */\r
4883     switch (LOWORD(wParam)) {\r
4884     case IDCANCEL:\r
4885       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4886       ClearHighlights();\r
4887       DrawPosition(FALSE, NULL);\r
4888       return TRUE;\r
4889     case PB_King:\r
4890       promoChar = PieceToChar(BlackKing);\r
4891       break;\r
4892     case PB_Queen:\r
4893       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4894       break;\r
4895     case PB_Rook:\r
4896       promoChar = PieceToChar(BlackRook);\r
4897       break;\r
4898     case PB_Bishop:\r
4899       promoChar = PieceToChar(BlackBishop);\r
4900       break;\r
4901     case PB_Chancellor:\r
4902       promoChar = PieceToChar(BlackMarshall);\r
4903       break;\r
4904     case PB_Archbishop:\r
4905       promoChar = PieceToChar(BlackCardinal);\r
4906       break;\r
4907     case PB_Knight:\r
4908       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4909       break;\r
4910     default:\r
4911       return FALSE;\r
4912     }\r
4913     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4914     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4915        only show the popup when we are already sure the move is valid or\r
4916        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4917        will figure out it is a promotion from the promoChar. */\r
4918     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
4919     if (!appData.highlightLastMove) {\r
4920       ClearHighlights();\r
4921       DrawPosition(FALSE, NULL);\r
4922     }\r
4923     return TRUE;\r
4924   }\r
4925   return FALSE;\r
4926 }\r
4927 \r
4928 /* Pop up promotion dialog */\r
4929 VOID\r
4930 PromotionPopup(HWND hwnd)\r
4931 {\r
4932   FARPROC lpProc;\r
4933 \r
4934   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4935   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4936     hwnd, (DLGPROC)lpProc);\r
4937   FreeProcInstance(lpProc);\r
4938 }\r
4939 \r
4940 /* Toggle ShowThinking */\r
4941 VOID\r
4942 ToggleShowThinking()\r
4943 {\r
4944   ShowThinkingEvent(!appData.showThinking);\r
4945 }\r
4946 \r
4947 VOID\r
4948 LoadGameDialog(HWND hwnd, char* title)\r
4949 {\r
4950   UINT number = 0;\r
4951   FILE *f;\r
4952   char fileTitle[MSG_SIZ];\r
4953   f = OpenFileDialog(hwnd, FALSE, "",\r
4954                      appData.oldSaveStyle ? "gam" : "pgn",\r
4955                      GAME_FILT,\r
4956                      title, &number, fileTitle, NULL);\r
4957   if (f != NULL) {\r
4958     cmailMsgLoaded = FALSE;\r
4959     if (number == 0) {\r
4960       int error = GameListBuild(f);\r
4961       if (error) {\r
4962         DisplayError("Cannot build game list", error);\r
4963       } else if (!ListEmpty(&gameList) &&\r
4964                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4965         GameListPopUp(f, fileTitle);\r
4966         return;\r
4967       }\r
4968       GameListDestroy();\r
4969       number = 1;\r
4970     }\r
4971     LoadGame(f, number, fileTitle, FALSE);\r
4972   }\r
4973 }\r
4974 \r
4975 VOID\r
4976 ChangedConsoleFont()\r
4977 {\r
4978   CHARFORMAT cfmt;\r
4979   CHARRANGE tmpsel, sel;\r
4980   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4981   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4982   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4983   PARAFORMAT paraf;\r
4984 \r
4985   cfmt.cbSize = sizeof(CHARFORMAT);\r
4986   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4987   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4988   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4989    * size.  This was undocumented in the version of MSVC++ that I had\r
4990    * when I wrote the code, but is apparently documented now.\r
4991    */\r
4992   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4993   cfmt.bCharSet = f->lf.lfCharSet;\r
4994   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4995   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4996   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4997   /* Why are the following seemingly needed too? */\r
4998   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4999   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5000   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5001   tmpsel.cpMin = 0;\r
5002   tmpsel.cpMax = -1; /*999999?*/\r
5003   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5004   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5005   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5006    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5007    */\r
5008   paraf.cbSize = sizeof(paraf);\r
5009   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5010   paraf.dxStartIndent = 0;\r
5011   paraf.dxOffset = WRAP_INDENT;\r
5012   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5013   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5014 }\r
5015 \r
5016 /*---------------------------------------------------------------------------*\\r
5017  *\r
5018  * Window Proc for main window\r
5019  *\r
5020 \*---------------------------------------------------------------------------*/\r
5021 \r
5022 /* Process messages for main window, etc. */\r
5023 LRESULT CALLBACK\r
5024 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5025 {\r
5026   FARPROC lpProc;\r
5027   int wmId, wmEvent;\r
5028   char *defName;\r
5029   FILE *f;\r
5030   UINT number;\r
5031   char fileTitle[MSG_SIZ];\r
5032   static SnapData sd;\r
5033 \r
5034   switch (message) {\r
5035 \r
5036   case WM_PAINT: /* message: repaint portion of window */\r
5037     PaintProc(hwnd);\r
5038     break;\r
5039 \r
5040   case WM_ERASEBKGND:\r
5041     if (IsIconic(hwnd)) {\r
5042       /* Cheat; change the message */\r
5043       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5044     } else {\r
5045       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5046     }\r
5047     break;\r
5048 \r
5049   case WM_LBUTTONDOWN:\r
5050   case WM_MBUTTONDOWN:\r
5051   case WM_RBUTTONDOWN:\r
5052   case WM_LBUTTONUP:\r
5053   case WM_MBUTTONUP:\r
5054   case WM_RBUTTONUP:\r
5055   case WM_MOUSEMOVE:\r
5056     MouseEvent(hwnd, message, wParam, lParam);\r
5057     break;\r
5058 \r
5059   case WM_CHAR:\r
5060     \r
5061     if (appData.icsActive) {\r
5062       if (wParam == '\t') {\r
5063         if (GetKeyState(VK_SHIFT) < 0) {\r
5064           /* shifted */\r
5065           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5066           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5067           SetFocus(h);\r
5068         } else {\r
5069           /* unshifted */\r
5070           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5071           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5072           SetFocus(h);\r
5073         }\r
5074       } else {\r
5075         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5076         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5077         SetFocus(h);\r
5078         SendMessage(h, message, wParam, lParam);\r
5079       }\r
5080     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5081       PopUpMoveDialog((char)wParam);\r
5082     }\r
5083     break;\r
5084 \r
5085   case WM_PALETTECHANGED:\r
5086     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5087       int nnew;\r
5088       HDC hdc = GetDC(hwndMain);\r
5089       SelectPalette(hdc, hPal, TRUE);\r
5090       nnew = RealizePalette(hdc);\r
5091       if (nnew > 0) {\r
5092         paletteChanged = TRUE;\r
5093 #if 0\r
5094         UpdateColors(hdc);\r
5095 #else\r
5096         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5097 #endif\r
5098       }\r
5099       ReleaseDC(hwnd, hdc);\r
5100     }\r
5101     break;\r
5102 \r
5103   case WM_QUERYNEWPALETTE:\r
5104     if (!appData.monoMode /*&& paletteChanged*/) {\r
5105       int nnew;\r
5106       HDC hdc = GetDC(hwndMain);\r
5107       paletteChanged = FALSE;\r
5108       SelectPalette(hdc, hPal, FALSE);\r
5109       nnew = RealizePalette(hdc);\r
5110       if (nnew > 0) {\r
5111         InvalidateRect(hwnd, &boardRect, FALSE);\r
5112       }\r
5113       ReleaseDC(hwnd, hdc);\r
5114       return TRUE;\r
5115     }\r
5116     return FALSE;\r
5117 \r
5118   case WM_COMMAND: /* message: command from application menu */\r
5119     wmId    = LOWORD(wParam);\r
5120     wmEvent = HIWORD(wParam);\r
5121 \r
5122     switch (wmId) {\r
5123     case IDM_NewGame:\r
5124       ResetGameEvent();\r
5125       AnalysisPopDown();\r
5126       break;\r
5127 \r
5128     case IDM_NewGameFRC:\r
5129       if( NewGameFRC() == 0 ) {\r
5130         ResetGameEvent();\r
5131         AnalysisPopDown();\r
5132       }\r
5133       break;\r
5134 \r
5135     case IDM_NewVariant:\r
5136       NewVariantPopup(hwnd);\r
5137       break;\r
5138 \r
5139     case IDM_LoadGame:\r
5140       LoadGameDialog(hwnd, "Load Game from File");\r
5141       break;\r
5142 \r
5143     case IDM_LoadNextGame:\r
5144       ReloadGame(1);\r
5145       break;\r
5146 \r
5147     case IDM_LoadPrevGame:\r
5148       ReloadGame(-1);\r
5149       break;\r
5150 \r
5151     case IDM_ReloadGame:\r
5152       ReloadGame(0);\r
5153       break;\r
5154 \r
5155     case IDM_LoadPosition:\r
5156       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5157         Reset(FALSE, TRUE);\r
5158       }\r
5159       number = 1;\r
5160       f = OpenFileDialog(hwnd, FALSE, "",\r
5161                          appData.oldSaveStyle ? "pos" : "fen",\r
5162                          POSITION_FILT,\r
5163                          "Load Position from File", &number, fileTitle, NULL);\r
5164       if (f != NULL) {\r
5165         LoadPosition(f, number, fileTitle);\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_LoadNextPosition:\r
5170       ReloadPosition(1);\r
5171       break;\r
5172 \r
5173     case IDM_LoadPrevPosition:\r
5174       ReloadPosition(-1);\r
5175       break;\r
5176 \r
5177     case IDM_ReloadPosition:\r
5178       ReloadPosition(0);\r
5179       break;\r
5180 \r
5181     case IDM_SaveGame:\r
5182       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5183       f = OpenFileDialog(hwnd, TRUE, defName,\r
5184                          appData.oldSaveStyle ? "gam" : "pgn",\r
5185                          GAME_FILT,\r
5186                          "Save Game to File", NULL, fileTitle, NULL);\r
5187       if (f != NULL) {\r
5188         SaveGame(f, 0, "");\r
5189       }\r
5190       break;\r
5191 \r
5192     case IDM_SavePosition:\r
5193       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5194       f = OpenFileDialog(hwnd, TRUE, defName,\r
5195                          appData.oldSaveStyle ? "pos" : "fen",\r
5196                          POSITION_FILT,\r
5197                          "Save Position to File", NULL, fileTitle, NULL);\r
5198       if (f != NULL) {\r
5199         SavePosition(f, 0, "");\r
5200       }\r
5201       break;\r
5202 \r
5203     case IDM_CopyGame:\r
5204       CopyGameToClipboard();\r
5205       break;\r
5206 \r
5207     case IDM_PasteGame:\r
5208       PasteGameFromClipboard();\r
5209       break;\r
5210 \r
5211     case IDM_CopyGameListToClipboard:\r
5212       CopyGameListToClipboard();\r
5213       break;\r
5214 \r
5215     /* [AS] Autodetect FEN or PGN data */\r
5216     case IDM_PasteAny:\r
5217       PasteGameOrFENFromClipboard();\r
5218       break;\r
5219 \r
5220     /* [AS] Move history */\r
5221     case IDM_ShowMoveHistory:\r
5222         if( MoveHistoryIsUp() ) {\r
5223             MoveHistoryPopDown();\r
5224         }\r
5225         else {\r
5226             MoveHistoryPopUp();\r
5227         }\r
5228         break;\r
5229 \r
5230     /* [AS] Eval graph */\r
5231     case IDM_ShowEvalGraph:\r
5232         if( EvalGraphIsUp() ) {\r
5233             EvalGraphPopDown();\r
5234         }\r
5235         else {\r
5236             EvalGraphPopUp();\r
5237         }\r
5238         break;\r
5239 \r
5240     /* [AS] Engine output */\r
5241     case IDM_ShowEngineOutput:\r
5242         if( EngineOutputIsUp() ) {\r
5243             EngineOutputPopDown();\r
5244         }\r
5245         else {\r
5246             EngineOutputPopUp();\r
5247         }\r
5248         break;\r
5249 \r
5250     /* [AS] User adjudication */\r
5251     case IDM_UserAdjudication_White:\r
5252         UserAdjudicationEvent( +1 );\r
5253         break;\r
5254 \r
5255     case IDM_UserAdjudication_Black:\r
5256         UserAdjudicationEvent( -1 );\r
5257         break;\r
5258 \r
5259     case IDM_UserAdjudication_Draw:\r
5260         UserAdjudicationEvent( 0 );\r
5261         break;\r
5262 \r
5263     /* [AS] Game list options dialog */\r
5264     case IDM_GameListOptions:\r
5265       GameListOptions();\r
5266       break;\r
5267 \r
5268     case IDM_CopyPosition:\r
5269       CopyFENToClipboard();\r
5270       break;\r
5271 \r
5272     case IDM_PastePosition:\r
5273       PasteFENFromClipboard();\r
5274       break;\r
5275 \r
5276     case IDM_MailMove:\r
5277       MailMoveEvent();\r
5278       break;\r
5279 \r
5280     case IDM_ReloadCMailMsg:\r
5281       Reset(TRUE, TRUE);\r
5282       ReloadCmailMsgEvent(FALSE);\r
5283       break;\r
5284 \r
5285     case IDM_Minimize:\r
5286       ShowWindow(hwnd, SW_MINIMIZE);\r
5287       break;\r
5288 \r
5289     case IDM_Exit:\r
5290       ExitEvent(0);\r
5291       break;\r
5292 \r
5293     case IDM_MachineWhite:\r
5294       MachineWhiteEvent();\r
5295       /*\r
5296        * refresh the tags dialog only if it's visible\r
5297        */\r
5298       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5299           char *tags;\r
5300           tags = PGNTags(&gameInfo);\r
5301           TagsPopUp(tags, CmailMsg());\r
5302           free(tags);\r
5303       }\r
5304       break;\r
5305 \r
5306     case IDM_MachineBlack:\r
5307       MachineBlackEvent();\r
5308       /*\r
5309        * refresh the tags dialog only if it's visible\r
5310        */\r
5311       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5312           char *tags;\r
5313           tags = PGNTags(&gameInfo);\r
5314           TagsPopUp(tags, CmailMsg());\r
5315           free(tags);\r
5316       }\r
5317       break;\r
5318 \r
5319     case IDM_TwoMachines:\r
5320       TwoMachinesEvent();\r
5321       /*\r
5322        * refresh the tags dialog only if it's visible\r
5323        */\r
5324       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5325           char *tags;\r
5326           tags = PGNTags(&gameInfo);\r
5327           TagsPopUp(tags, CmailMsg());\r
5328           free(tags);\r
5329       }\r
5330       break;\r
5331 \r
5332     case IDM_AnalysisMode:\r
5333       if (!first.analysisSupport) {\r
5334         char buf[MSG_SIZ];\r
5335         sprintf(buf, "%s does not support analysis", first.tidy);\r
5336         DisplayError(buf, 0);\r
5337       } else {\r
5338         if (!appData.showThinking) ToggleShowThinking();\r
5339         AnalyzeModeEvent();\r
5340       }\r
5341       break;\r
5342 \r
5343     case IDM_AnalyzeFile:\r
5344       if (!first.analysisSupport) {\r
5345         char buf[MSG_SIZ];\r
5346         sprintf(buf, "%s does not support analysis", first.tidy);\r
5347         DisplayError(buf, 0);\r
5348       } else {\r
5349         if (!appData.showThinking) ToggleShowThinking();\r
5350         AnalyzeFileEvent();\r
5351         LoadGameDialog(hwnd, "Analyze Game from File");\r
5352         AnalysisPeriodicEvent(1);\r
5353       }\r
5354       break;\r
5355 \r
5356     case IDM_IcsClient:\r
5357       IcsClientEvent();\r
5358       break;\r
5359 \r
5360     case IDM_EditGame:\r
5361       EditGameEvent();\r
5362       break;\r
5363 \r
5364     case IDM_EditPosition:\r
5365       EditPositionEvent();\r
5366       break;\r
5367 \r
5368     case IDM_Training:\r
5369       TrainingEvent();\r
5370       break;\r
5371 \r
5372     case IDM_ShowGameList:\r
5373       ShowGameListProc();\r
5374       break;\r
5375 \r
5376     case IDM_EditTags:\r
5377       EditTagsProc();\r
5378       break;\r
5379 \r
5380     case IDM_EditComment:\r
5381       if (commentDialogUp && editComment) {\r
5382         CommentPopDown();\r
5383       } else {\r
5384         EditCommentEvent();\r
5385       }\r
5386       break;\r
5387 \r
5388     case IDM_Pause:\r
5389       PauseEvent();\r
5390       break;\r
5391 \r
5392     case IDM_Accept:\r
5393       AcceptEvent();\r
5394       break;\r
5395 \r
5396     case IDM_Decline:\r
5397       DeclineEvent();\r
5398       break;\r
5399 \r
5400     case IDM_Rematch:\r
5401       RematchEvent();\r
5402       break;\r
5403 \r
5404     case IDM_CallFlag:\r
5405       CallFlagEvent();\r
5406       break;\r
5407 \r
5408     case IDM_Draw:\r
5409       DrawEvent();\r
5410       break;\r
5411 \r
5412     case IDM_Adjourn:\r
5413       AdjournEvent();\r
5414       break;\r
5415 \r
5416     case IDM_Abort:\r
5417       AbortEvent();\r
5418       break;\r
5419 \r
5420     case IDM_Resign:\r
5421       ResignEvent();\r
5422       break;\r
5423 \r
5424     case IDM_StopObserving:\r
5425       StopObservingEvent();\r
5426       break;\r
5427 \r
5428     case IDM_StopExamining:\r
5429       StopExaminingEvent();\r
5430       break;\r
5431 \r
5432     case IDM_TypeInMove:\r
5433       PopUpMoveDialog('\000');\r
5434       break;\r
5435 \r
5436     case IDM_Backward:\r
5437       BackwardEvent();\r
5438       SetFocus(hwndMain);\r
5439       break;\r
5440 \r
5441     case IDM_Forward:\r
5442       ForwardEvent();\r
5443       SetFocus(hwndMain);\r
5444       break;\r
5445 \r
5446     case IDM_ToStart:\r
5447       ToStartEvent();\r
5448       SetFocus(hwndMain);\r
5449       break;\r
5450 \r
5451     case IDM_ToEnd:\r
5452       ToEndEvent();\r
5453       SetFocus(hwndMain);\r
5454       break;\r
5455 \r
5456     case IDM_Revert:\r
5457       RevertEvent();\r
5458       break;\r
5459 \r
5460     case IDM_TruncateGame:\r
5461       TruncateGameEvent();\r
5462       break;\r
5463 \r
5464     case IDM_MoveNow:\r
5465       MoveNowEvent();\r
5466       break;\r
5467 \r
5468     case IDM_RetractMove:\r
5469       RetractMoveEvent();\r
5470       break;\r
5471 \r
5472     case IDM_FlipView:\r
5473       flipView = !flipView;\r
5474       DrawPosition(FALSE, NULL);\r
5475       break;\r
5476 \r
5477     case IDM_FlipClock:\r
5478       flipClock = !flipClock;\r
5479       DisplayBothClocks();\r
5480       break;\r
5481 \r
5482     case IDM_GeneralOptions:\r
5483       GeneralOptionsPopup(hwnd);\r
5484       DrawPosition(TRUE, NULL);\r
5485       break;\r
5486 \r
5487     case IDM_BoardOptions:\r
5488       BoardOptionsPopup(hwnd);\r
5489       break;\r
5490 \r
5491     case IDM_EnginePlayOptions:\r
5492       EnginePlayOptionsPopup(hwnd);\r
5493       break;\r
5494 \r
5495     case IDM_OptionsUCI:\r
5496       UciOptionsPopup(hwnd);\r
5497       break;\r
5498 \r
5499     case IDM_IcsOptions:\r
5500       IcsOptionsPopup(hwnd);\r
5501       break;\r
5502 \r
5503     case IDM_Fonts:\r
5504       FontsOptionsPopup(hwnd);\r
5505       break;\r
5506 \r
5507     case IDM_Sounds:\r
5508       SoundOptionsPopup(hwnd);\r
5509       break;\r
5510 \r
5511     case IDM_CommPort:\r
5512       CommPortOptionsPopup(hwnd);\r
5513       break;\r
5514 \r
5515     case IDM_LoadOptions:\r
5516       LoadOptionsPopup(hwnd);\r
5517       break;\r
5518 \r
5519     case IDM_SaveOptions:\r
5520       SaveOptionsPopup(hwnd);\r
5521       break;\r
5522 \r
5523     case IDM_TimeControl:\r
5524       TimeControlOptionsPopup(hwnd);\r
5525       break;\r
5526 \r
5527     case IDM_SaveSettings:\r
5528       SaveSettings(settingsFileName);\r
5529       break;\r
5530 \r
5531     case IDM_SaveSettingsOnExit:\r
5532       saveSettingsOnExit = !saveSettingsOnExit;\r
5533       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5534                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5535                                          MF_CHECKED : MF_UNCHECKED));\r
5536       break;\r
5537 \r
5538     case IDM_Hint:\r
5539       HintEvent();\r
5540       break;\r
5541 \r
5542     case IDM_Book:\r
5543       BookEvent();\r
5544       break;\r
5545 \r
5546     case IDM_AboutGame:\r
5547       AboutGameEvent();\r
5548       break;\r
5549 \r
5550     case IDM_Debug:\r
5551       appData.debugMode = !appData.debugMode;\r
5552       if (appData.debugMode) {\r
5553         char dir[MSG_SIZ];\r
5554         GetCurrentDirectory(MSG_SIZ, dir);\r
5555         SetCurrentDirectory(installDir);\r
5556         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5557         SetCurrentDirectory(dir);\r
5558         setbuf(debugFP, NULL);\r
5559       } else {\r
5560         fclose(debugFP);\r
5561         debugFP = NULL;\r
5562       }\r
5563       break;\r
5564 \r
5565     case IDM_HELPCONTENTS:\r
5566       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5567         MessageBox (GetFocus(),\r
5568                     "Unable to activate help",\r
5569                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5570       }\r
5571       break;\r
5572 \r
5573     case IDM_HELPSEARCH:\r
5574       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5575         MessageBox (GetFocus(),\r
5576                     "Unable to activate help",\r
5577                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5578       }\r
5579       break;\r
5580 \r
5581     case IDM_HELPHELP:\r
5582       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5583         MessageBox (GetFocus(),\r
5584                     "Unable to activate help",\r
5585                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5586       }\r
5587       break;\r
5588 \r
5589     case IDM_ABOUT:\r
5590       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5591       DialogBox(hInst, \r
5592         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5593         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5594       FreeProcInstance(lpProc);\r
5595       break;\r
5596 \r
5597     case IDM_DirectCommand1:\r
5598       AskQuestionEvent("Direct Command",\r
5599                        "Send to chess program:", "", "1");\r
5600       break;\r
5601     case IDM_DirectCommand2:\r
5602       AskQuestionEvent("Direct Command",\r
5603                        "Send to second chess program:", "", "2");\r
5604       break;\r
5605 \r
5606     case EP_WhitePawn:\r
5607       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5608       fromX = fromY = -1;\r
5609       break;\r
5610 \r
5611     case EP_WhiteKnight:\r
5612       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5613       fromX = fromY = -1;\r
5614       break;\r
5615 \r
5616     case EP_WhiteBishop:\r
5617       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5618       fromX = fromY = -1;\r
5619       break;\r
5620 \r
5621     case EP_WhiteRook:\r
5622       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5623       fromX = fromY = -1;\r
5624       break;\r
5625 \r
5626     case EP_WhiteQueen:\r
5627       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5628       fromX = fromY = -1;\r
5629       break;\r
5630 \r
5631     case EP_WhiteFerz:\r
5632       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5633       fromX = fromY = -1;\r
5634       break;\r
5635 \r
5636     case EP_WhiteWazir:\r
5637       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5638       fromX = fromY = -1;\r
5639       break;\r
5640 \r
5641     case EP_WhiteAlfil:\r
5642       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5643       fromX = fromY = -1;\r
5644       break;\r
5645 \r
5646     case EP_WhiteCannon:\r
5647       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5648       fromX = fromY = -1;\r
5649       break;\r
5650 \r
5651     case EP_WhiteCardinal:\r
5652       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5653       fromX = fromY = -1;\r
5654       break;\r
5655 \r
5656     case EP_WhiteMarshall:\r
5657       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5658       fromX = fromY = -1;\r
5659       break;\r
5660 \r
5661     case EP_WhiteKing:\r
5662       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5663       fromX = fromY = -1;\r
5664       break;\r
5665 \r
5666     case EP_BlackPawn:\r
5667       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5668       fromX = fromY = -1;\r
5669       break;\r
5670 \r
5671     case EP_BlackKnight:\r
5672       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5673       fromX = fromY = -1;\r
5674       break;\r
5675 \r
5676     case EP_BlackBishop:\r
5677       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5678       fromX = fromY = -1;\r
5679       break;\r
5680 \r
5681     case EP_BlackRook:\r
5682       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5683       fromX = fromY = -1;\r
5684       break;\r
5685 \r
5686     case EP_BlackQueen:\r
5687       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5688       fromX = fromY = -1;\r
5689       break;\r
5690 \r
5691     case EP_BlackFerz:\r
5692       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5693       fromX = fromY = -1;\r
5694       break;\r
5695 \r
5696     case EP_BlackWazir:\r
5697       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5698       fromX = fromY = -1;\r
5699       break;\r
5700 \r
5701     case EP_BlackAlfil:\r
5702       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5703       fromX = fromY = -1;\r
5704       break;\r
5705 \r
5706     case EP_BlackCannon:\r
5707       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5708       fromX = fromY = -1;\r
5709       break;\r
5710 \r
5711     case EP_BlackCardinal:\r
5712       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5713       fromX = fromY = -1;\r
5714       break;\r
5715 \r
5716     case EP_BlackMarshall:\r
5717       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5718       fromX = fromY = -1;\r
5719       break;\r
5720 \r
5721     case EP_BlackKing:\r
5722       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5723       fromX = fromY = -1;\r
5724       break;\r
5725 \r
5726     case EP_EmptySquare:\r
5727       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5728       fromX = fromY = -1;\r
5729       break;\r
5730 \r
5731     case EP_ClearBoard:\r
5732       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5733       fromX = fromY = -1;\r
5734       break;\r
5735 \r
5736     case EP_White:\r
5737       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5738       fromX = fromY = -1;\r
5739       break;\r
5740 \r
5741     case EP_Black:\r
5742       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5743       fromX = fromY = -1;\r
5744       break;\r
5745 \r
5746     case EP_Promote:\r
5747       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5748       fromX = fromY = -1;\r
5749       break;\r
5750 \r
5751     case EP_Demote:\r
5752       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5753       fromX = fromY = -1;\r
5754       break;\r
5755 \r
5756     case DP_Pawn:\r
5757       DropMenuEvent(WhitePawn, fromX, fromY);\r
5758       fromX = fromY = -1;\r
5759       break;\r
5760 \r
5761     case DP_Knight:\r
5762       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5763       fromX = fromY = -1;\r
5764       break;\r
5765 \r
5766     case DP_Bishop:\r
5767       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5768       fromX = fromY = -1;\r
5769       break;\r
5770 \r
5771     case DP_Rook:\r
5772       DropMenuEvent(WhiteRook, fromX, fromY);\r
5773       fromX = fromY = -1;\r
5774       break;\r
5775 \r
5776     case DP_Queen:\r
5777       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5778       fromX = fromY = -1;\r
5779       break;\r
5780 \r
5781     default:\r
5782       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5783     }\r
5784     break;\r
5785 \r
5786   case WM_TIMER:\r
5787     switch (wParam) {\r
5788     case CLOCK_TIMER_ID:\r
5789       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5790       clockTimerEvent = 0;\r
5791       DecrementClocks(); /* call into back end */\r
5792       break;\r
5793     case LOAD_GAME_TIMER_ID:\r
5794       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5795       loadGameTimerEvent = 0;\r
5796       AutoPlayGameLoop(); /* call into back end */\r
5797       break;\r
5798     case ANALYSIS_TIMER_ID:\r
5799       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5800           appData.periodicUpdates) {\r
5801         AnalysisPeriodicEvent(0);\r
5802       } else {\r
5803         KillTimer(hwnd, analysisTimerEvent);\r
5804         analysisTimerEvent = 0;\r
5805       }\r
5806       break;\r
5807     case DELAYED_TIMER_ID:\r
5808       KillTimer(hwnd, delayedTimerEvent);\r
5809       delayedTimerEvent = 0;\r
5810       delayedTimerCallback();\r
5811       break;\r
5812     }\r
5813     break;\r
5814 \r
5815   case WM_USER_Input:\r
5816     InputEvent(hwnd, message, wParam, lParam);\r
5817     break;\r
5818 \r
5819   /* [AS] Also move "attached" child windows */\r
5820   case WM_WINDOWPOSCHANGING:\r
5821     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5822         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5823 \r
5824         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5825             /* Window is moving */\r
5826             RECT rcMain;\r
5827 \r
5828             GetWindowRect( hwnd, &rcMain );\r
5829             \r
5830             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5831             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5832             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5833         }\r
5834     }\r
5835     break;\r
5836 \r
5837   /* [AS] Snapping */\r
5838   case WM_ENTERSIZEMOVE:\r
5839     if (hwnd == hwndMain) {\r
5840       doingSizing = TRUE;\r
5841       lastSizing = 0;\r
5842     }\r
5843     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5844     break;\r
5845 \r
5846   case WM_SIZING:\r
5847     if (hwnd == hwndMain) {\r
5848       lastSizing = wParam;\r
5849     }\r
5850     break;\r
5851 \r
5852   case WM_MOVING:\r
5853       return OnMoving( &sd, hwnd, wParam, lParam );\r
5854 \r
5855   case WM_EXITSIZEMOVE:\r
5856     if (hwnd == hwndMain) {\r
5857       RECT client;\r
5858       doingSizing = FALSE;\r
5859       InvalidateRect(hwnd, &boardRect, FALSE);\r
5860       GetClientRect(hwnd, &client);\r
5861       ResizeBoard(client.right, client.bottom, lastSizing);\r
5862       lastSizing = 0;\r
5863     }\r
5864     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5865     break;\r
5866 \r
5867   case WM_DESTROY: /* message: window being destroyed */\r
5868     PostQuitMessage(0);\r
5869     break;\r
5870 \r
5871   case WM_CLOSE:\r
5872     if (hwnd == hwndMain) {\r
5873       ExitEvent(0);\r
5874     }\r
5875     break;\r
5876 \r
5877   default:      /* Passes it on if unprocessed */\r
5878     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5879   }\r
5880   return 0;\r
5881 }\r
5882 \r
5883 /*---------------------------------------------------------------------------*\\r
5884  *\r
5885  * Misc utility routines\r
5886  *\r
5887 \*---------------------------------------------------------------------------*/\r
5888 \r
5889 /*\r
5890  * Decent random number generator, at least not as bad as Windows\r
5891  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5892  */\r
5893 unsigned int randstate;\r
5894 \r
5895 int\r
5896 myrandom(void)\r
5897 {\r
5898   randstate = randstate * 1664525 + 1013904223;\r
5899   return (int) randstate & 0x7fffffff;\r
5900 }\r
5901 \r
5902 void\r
5903 mysrandom(unsigned int seed)\r
5904 {\r
5905   randstate = seed;\r
5906 }\r
5907 \r
5908 \r
5909 /* \r
5910  * returns TRUE if user selects a different color, FALSE otherwise \r
5911  */\r
5912 \r
5913 BOOL\r
5914 ChangeColor(HWND hwnd, COLORREF *which)\r
5915 {\r
5916   static BOOL firstTime = TRUE;\r
5917   static DWORD customColors[16];\r
5918   CHOOSECOLOR cc;\r
5919   COLORREF newcolor;\r
5920   int i;\r
5921   ColorClass ccl;\r
5922 \r
5923   if (firstTime) {\r
5924     /* Make initial colors in use available as custom colors */\r
5925     /* Should we put the compiled-in defaults here instead? */\r
5926     i = 0;\r
5927     customColors[i++] = lightSquareColor & 0xffffff;\r
5928     customColors[i++] = darkSquareColor & 0xffffff;\r
5929     customColors[i++] = whitePieceColor & 0xffffff;\r
5930     customColors[i++] = blackPieceColor & 0xffffff;\r
5931     customColors[i++] = highlightSquareColor & 0xffffff;\r
5932     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5933 \r
5934     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5935       customColors[i++] = textAttribs[ccl].color;\r
5936     }\r
5937     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5938     firstTime = FALSE;\r
5939   }\r
5940 \r
5941   cc.lStructSize = sizeof(cc);\r
5942   cc.hwndOwner = hwnd;\r
5943   cc.hInstance = NULL;\r
5944   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5945   cc.lpCustColors = (LPDWORD) customColors;\r
5946   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5947 \r
5948   if (!ChooseColor(&cc)) return FALSE;\r
5949 \r
5950   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5951   if (newcolor == *which) return FALSE;\r
5952   *which = newcolor;\r
5953   return TRUE;\r
5954 \r
5955   /*\r
5956   InitDrawingColors();\r
5957   InvalidateRect(hwnd, &boardRect, FALSE);\r
5958   */\r
5959 }\r
5960 \r
5961 BOOLEAN\r
5962 MyLoadSound(MySound *ms)\r
5963 {\r
5964   BOOL ok = FALSE;\r
5965   struct stat st;\r
5966   FILE *f;\r
5967 \r
5968   if (ms->data) free(ms->data);\r
5969   ms->data = NULL;\r
5970 \r
5971   switch (ms->name[0]) {\r
5972   case NULLCHAR:\r
5973     /* Silence */\r
5974     ok = TRUE;\r
5975     break;\r
5976   case '$':\r
5977     /* System sound from Control Panel.  Don't preload here. */\r
5978     ok = TRUE;\r
5979     break;\r
5980   case '!':\r
5981     if (ms->name[1] == NULLCHAR) {\r
5982       /* "!" alone = silence */\r
5983       ok = TRUE;\r
5984     } else {\r
5985       /* Builtin wave resource.  Error if not found. */\r
5986       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5987       if (h == NULL) break;\r
5988       ms->data = (void *)LoadResource(hInst, h);\r
5989       if (h == NULL) break;\r
5990       ok = TRUE;\r
5991     }\r
5992     break;\r
5993   default:\r
5994     /* .wav file.  Error if not found. */\r
5995     f = fopen(ms->name, "rb");\r
5996     if (f == NULL) break;\r
5997     if (fstat(fileno(f), &st) < 0) break;\r
5998     ms->data = malloc(st.st_size);\r
5999     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6000     fclose(f);\r
6001     ok = TRUE;\r
6002     break;\r
6003   }\r
6004   if (!ok) {\r
6005     char buf[MSG_SIZ];\r
6006     sprintf(buf, "Error loading sound %s", ms->name);\r
6007     DisplayError(buf, GetLastError());\r
6008   }\r
6009   return ok;\r
6010 }\r
6011 \r
6012 BOOLEAN\r
6013 MyPlaySound(MySound *ms)\r
6014 {\r
6015   BOOLEAN ok = FALSE;\r
6016   switch (ms->name[0]) {\r
6017   case NULLCHAR:\r
6018     /* Silence */\r
6019     ok = TRUE;\r
6020     break;\r
6021   case '$':\r
6022     /* System sound from Control Panel (deprecated feature).\r
6023        "$" alone or an unset sound name gets default beep (still in use). */\r
6024     if (ms->name[1]) {\r
6025       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6026     }\r
6027     if (!ok) ok = MessageBeep(MB_OK);\r
6028     break; \r
6029   case '!':\r
6030     /* Builtin wave resource, or "!" alone for silence */\r
6031     if (ms->name[1]) {\r
6032       if (ms->data == NULL) return FALSE;\r
6033       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6034     } else {\r
6035       ok = TRUE;\r
6036     }\r
6037     break;\r
6038   default:\r
6039     /* .wav file.  Error if not found. */\r
6040     if (ms->data == NULL) return FALSE;\r
6041     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6042     break;\r
6043   }\r
6044   /* Don't print an error: this can happen innocently if the sound driver\r
6045      is busy; for instance, if another instance of WinBoard is playing\r
6046      a sound at about the same time. */\r
6047 #if 0\r
6048   if (!ok) {\r
6049     char buf[MSG_SIZ];\r
6050     sprintf(buf, "Error playing sound %s", ms->name);\r
6051     DisplayError(buf, GetLastError());\r
6052   }\r
6053 #endif\r
6054   return ok;\r
6055 }\r
6056 \r
6057 \r
6058 LRESULT CALLBACK\r
6059 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6060 {\r
6061   BOOL ok;\r
6062   OPENFILENAME *ofn;\r
6063   static UINT *number; /* gross that this is static */\r
6064 \r
6065   switch (message) {\r
6066   case WM_INITDIALOG: /* message: initialize dialog box */\r
6067     /* Center the dialog over the application window */\r
6068     ofn = (OPENFILENAME *) lParam;\r
6069     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6070       number = (UINT *) ofn->lCustData;\r
6071       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6072     } else {\r
6073       number = NULL;\r
6074     }\r
6075     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6076     return FALSE;  /* Allow for further processing */\r
6077 \r
6078   case WM_COMMAND:\r
6079     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6080       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6081     }\r
6082     return FALSE;  /* Allow for further processing */\r
6083   }\r
6084   return FALSE;\r
6085 }\r
6086 \r
6087 UINT APIENTRY\r
6088 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6089 {\r
6090   static UINT *number;\r
6091   OPENFILENAME *ofname;\r
6092   OFNOTIFY *ofnot;\r
6093   switch (uiMsg) {\r
6094   case WM_INITDIALOG:\r
6095     ofname = (OPENFILENAME *)lParam;\r
6096     number = (UINT *)(ofname->lCustData);\r
6097     break;\r
6098   case WM_NOTIFY:\r
6099     ofnot = (OFNOTIFY *)lParam;\r
6100     if (ofnot->hdr.code == CDN_FILEOK) {\r
6101       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6102     }\r
6103     break;\r
6104   }\r
6105   return 0;\r
6106 }\r
6107 \r
6108 \r
6109 FILE *\r
6110 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
6111                char *nameFilt, char *dlgTitle, UINT *number,\r
6112                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6113 {\r
6114   OPENFILENAME openFileName;\r
6115   char buf1[MSG_SIZ];\r
6116   FILE *f;\r
6117 \r
6118   if (fileName == NULL) fileName = buf1;\r
6119   if (defName == NULL) {\r
6120     strcpy(fileName, "*.");\r
6121     strcat(fileName, defExt);\r
6122   } else {\r
6123     strcpy(fileName, defName);\r
6124   }\r
6125   if (fileTitle) strcpy(fileTitle, "");\r
6126   if (number) *number = 0;\r
6127 \r
6128   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6129   openFileName.hwndOwner         = hwnd;\r
6130   openFileName.hInstance         = (HANDLE) hInst;\r
6131   openFileName.lpstrFilter       = nameFilt;\r
6132   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6133   openFileName.nMaxCustFilter    = 0L;\r
6134   openFileName.nFilterIndex      = 1L;\r
6135   openFileName.lpstrFile         = fileName;\r
6136   openFileName.nMaxFile          = MSG_SIZ;\r
6137   openFileName.lpstrFileTitle    = fileTitle;\r
6138   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6139   openFileName.lpstrInitialDir   = NULL;\r
6140   openFileName.lpstrTitle        = dlgTitle;\r
6141   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6142     | (write ? 0 : OFN_FILEMUSTEXIST) \r
6143     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6144     | (oldDialog ? 0 : OFN_EXPLORER);\r
6145   openFileName.nFileOffset       = 0;\r
6146   openFileName.nFileExtension    = 0;\r
6147   openFileName.lpstrDefExt       = defExt;\r
6148   openFileName.lCustData         = (LONG) number;\r
6149   openFileName.lpfnHook          = oldDialog ?\r
6150     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6151   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6152 \r
6153   if (write ? GetSaveFileName(&openFileName) : \r
6154               GetOpenFileName(&openFileName)) {\r
6155     /* open the file */\r
6156     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
6157     if (f == NULL) {\r
6158       MessageBox(hwnd, "File open failed", NULL,\r
6159                  MB_OK|MB_ICONEXCLAMATION);\r
6160       return NULL;\r
6161     }\r
6162   } else {\r
6163     int err = CommDlgExtendedError();\r
6164     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6165     return FALSE;\r
6166   }\r
6167   return f;\r
6168 }\r
6169 \r
6170 \r
6171 \r
6172 VOID APIENTRY\r
6173 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6174 {\r
6175   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6176 \r
6177   /*\r
6178    * Get the first pop-up menu in the menu template. This is the\r
6179    * menu that TrackPopupMenu displays.\r
6180    */\r
6181   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6182 \r
6183   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6184 \r
6185   /*\r
6186    * TrackPopup uses screen coordinates, so convert the\r
6187    * coordinates of the mouse click to screen coordinates.\r
6188    */\r
6189   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6190 \r
6191   /* Draw and track the floating pop-up menu. */\r
6192   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6193                  pt.x, pt.y, 0, hwnd, NULL);\r
6194 \r
6195   /* Destroy the menu.*/\r
6196   DestroyMenu(hmenu);\r
6197 }\r
6198    \r
6199 typedef struct {\r
6200   HWND hDlg, hText;\r
6201   int sizeX, sizeY, newSizeX, newSizeY;\r
6202   HDWP hdwp;\r
6203 } ResizeEditPlusButtonsClosure;\r
6204 \r
6205 BOOL CALLBACK\r
6206 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6207 {\r
6208   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6209   RECT rect;\r
6210   POINT pt;\r
6211 \r
6212   if (hChild == cl->hText) return TRUE;\r
6213   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6214   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6215   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6216   ScreenToClient(cl->hDlg, &pt);\r
6217   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6218     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6219   return TRUE;\r
6220 }\r
6221 \r
6222 /* Resize a dialog that has a (rich) edit field filling most of\r
6223    the top, with a row of buttons below */\r
6224 VOID\r
6225 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6226 {\r
6227   RECT rectText;\r
6228   int newTextHeight, newTextWidth;\r
6229   ResizeEditPlusButtonsClosure cl;\r
6230   \r
6231   /*if (IsIconic(hDlg)) return;*/\r
6232   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6233   \r
6234   cl.hdwp = BeginDeferWindowPos(8);\r
6235 \r
6236   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6237   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6238   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6239   if (newTextHeight < 0) {\r
6240     newSizeY += -newTextHeight;\r
6241     newTextHeight = 0;\r
6242   }\r
6243   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6244     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6245 \r
6246   cl.hDlg = hDlg;\r
6247   cl.hText = hText;\r
6248   cl.sizeX = sizeX;\r
6249   cl.sizeY = sizeY;\r
6250   cl.newSizeX = newSizeX;\r
6251   cl.newSizeY = newSizeY;\r
6252   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6253 \r
6254   EndDeferWindowPos(cl.hdwp);\r
6255 }\r
6256 \r
6257 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6258 {\r
6259     RECT    rChild, rParent;\r
6260     int     wChild, hChild, wParent, hParent;\r
6261     int     wScreen, hScreen, xNew, yNew;\r
6262     HDC     hdc;\r
6263 \r
6264     /* Get the Height and Width of the child window */\r
6265     GetWindowRect (hwndChild, &rChild);\r
6266     wChild = rChild.right - rChild.left;\r
6267     hChild = rChild.bottom - rChild.top;\r
6268 \r
6269     /* Get the Height and Width of the parent window */\r
6270     GetWindowRect (hwndParent, &rParent);\r
6271     wParent = rParent.right - rParent.left;\r
6272     hParent = rParent.bottom - rParent.top;\r
6273 \r
6274     /* Get the display limits */\r
6275     hdc = GetDC (hwndChild);\r
6276     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6277     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6278     ReleaseDC(hwndChild, hdc);\r
6279 \r
6280     /* Calculate new X position, then adjust for screen */\r
6281     xNew = rParent.left + ((wParent - wChild) /2);\r
6282     if (xNew < 0) {\r
6283         xNew = 0;\r
6284     } else if ((xNew+wChild) > wScreen) {\r
6285         xNew = wScreen - wChild;\r
6286     }\r
6287 \r
6288     /* Calculate new Y position, then adjust for screen */\r
6289     if( mode == 0 ) {\r
6290         yNew = rParent.top  + ((hParent - hChild) /2);\r
6291     }\r
6292     else {\r
6293         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6294     }\r
6295 \r
6296     if (yNew < 0) {\r
6297         yNew = 0;\r
6298     } else if ((yNew+hChild) > hScreen) {\r
6299         yNew = hScreen - hChild;\r
6300     }\r
6301 \r
6302     /* Set it, and return */\r
6303     return SetWindowPos (hwndChild, NULL,\r
6304                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6305 }\r
6306 \r
6307 /* Center one window over another */\r
6308 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6309 {\r
6310     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6311 }\r
6312 \r
6313 /*---------------------------------------------------------------------------*\\r
6314  *\r
6315  * Startup Dialog functions\r
6316  *\r
6317 \*---------------------------------------------------------------------------*/\r
6318 void\r
6319 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6320 {\r
6321   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6322 \r
6323   while (*cd != NULL) {\r
6324     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6325     cd++;\r
6326   }\r
6327 }\r
6328 \r
6329 void\r
6330 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6331 {\r
6332   char buf1[ARG_MAX];\r
6333   int len;\r
6334 \r
6335   if (str[0] == '@') {\r
6336     FILE* f = fopen(str + 1, "r");\r
6337     if (f == NULL) {\r
6338       DisplayFatalError(str + 1, errno, 2);\r
6339       return;\r
6340     }\r
6341     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6342     fclose(f);\r
6343     buf1[len] = NULLCHAR;\r
6344     str = buf1;\r
6345   }\r
6346 \r
6347   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6348 \r
6349   for (;;) {\r
6350     char buf[MSG_SIZ];\r
6351     char *end = strchr(str, '\n');\r
6352     if (end == NULL) return;\r
6353     memcpy(buf, str, end - str);\r
6354     buf[end - str] = NULLCHAR;\r
6355     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6356     str = end + 1;\r
6357   }\r
6358 }\r
6359 \r
6360 void\r
6361 SetStartupDialogEnables(HWND hDlg)\r
6362 {\r
6363   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6364     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6365     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6366   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6367     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6368   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6369     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6370   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6371     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6372   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6373     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6374     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6375     IsDlgButtonChecked(hDlg, OPT_View));\r
6376 }\r
6377 \r
6378 char *\r
6379 QuoteForFilename(char *filename)\r
6380 {\r
6381   int dquote, space;\r
6382   dquote = strchr(filename, '"') != NULL;\r
6383   space = strchr(filename, ' ') != NULL;\r
6384   if (dquote || space) {\r
6385     if (dquote) {\r
6386       return "'";\r
6387     } else {\r
6388       return "\"";\r
6389     }\r
6390   } else {\r
6391     return "";\r
6392   }\r
6393 }\r
6394 \r
6395 VOID\r
6396 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6397 {\r
6398   char buf[MSG_SIZ];\r
6399   char *q;\r
6400 \r
6401   InitComboStringsFromOption(hwndCombo, nthnames);\r
6402   q = QuoteForFilename(nthcp);\r
6403   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6404   if (*nthdir != NULLCHAR) {\r
6405     q = QuoteForFilename(nthdir);\r
6406     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6407   }\r
6408   if (*nthcp == NULLCHAR) {\r
6409     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6410   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6411     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6412     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6413   }\r
6414 }\r
6415 \r
6416 LRESULT CALLBACK\r
6417 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6418 {\r
6419   char buf[MSG_SIZ];\r
6420   HANDLE hwndCombo;\r
6421   char *p;\r
6422 \r
6423   switch (message) {\r
6424   case WM_INITDIALOG:\r
6425     /* Center the dialog */\r
6426     CenterWindow (hDlg, GetDesktopWindow());\r
6427     /* Initialize the dialog items */\r
6428     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6429                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6430                   firstChessProgramNames);\r
6431     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6432                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6433                   secondChessProgramNames);\r
6434     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6435     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6436     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6437     if (*appData.icsHelper != NULLCHAR) {\r
6438       char *q = QuoteForFilename(appData.icsHelper);\r
6439       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6440     }\r
6441     if (*appData.icsHost == NULLCHAR) {\r
6442       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6443       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6444     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6445       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6446       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6447     }\r
6448 \r
6449     if (appData.icsActive) {\r
6450       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6451     }\r
6452     else if (appData.noChessProgram) {\r
6453       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6454     }\r
6455     else {\r
6456       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6457     }\r
6458 \r
6459     SetStartupDialogEnables(hDlg);\r
6460     return TRUE;\r
6461 \r
6462   case WM_COMMAND:\r
6463     switch (LOWORD(wParam)) {\r
6464     case IDOK:\r
6465       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6466         strcpy(buf, "/fcp=");\r
6467         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6468         p = buf;\r
6469         ParseArgs(StringGet, &p);\r
6470         strcpy(buf, "/scp=");\r
6471         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6472         p = buf;\r
6473         ParseArgs(StringGet, &p);\r
6474         appData.noChessProgram = FALSE;\r
6475         appData.icsActive = FALSE;\r
6476       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6477         strcpy(buf, "/ics /icshost=");\r
6478         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6479         p = buf;\r
6480         ParseArgs(StringGet, &p);\r
6481         if (appData.zippyPlay) {\r
6482           strcpy(buf, "/fcp=");\r
6483           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6484           p = buf;\r
6485           ParseArgs(StringGet, &p);\r
6486         }\r
6487       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6488         appData.noChessProgram = TRUE;\r
6489         appData.icsActive = FALSE;\r
6490       } else {\r
6491         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6492                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6493         return TRUE;\r
6494       }\r
6495       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6496         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6497         p = buf;\r
6498         ParseArgs(StringGet, &p);\r
6499       }\r
6500       EndDialog(hDlg, TRUE);\r
6501       return TRUE;\r
6502 \r
6503     case IDCANCEL:\r
6504       ExitEvent(0);\r
6505       return TRUE;\r
6506 \r
6507     case IDM_HELPCONTENTS:\r
6508       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6509         MessageBox (GetFocus(),\r
6510                     "Unable to activate help",\r
6511                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6512       }\r
6513       break;\r
6514 \r
6515     default:\r
6516       SetStartupDialogEnables(hDlg);\r
6517       break;\r
6518     }\r
6519     break;\r
6520   }\r
6521   return FALSE;\r
6522 }\r
6523 \r
6524 /*---------------------------------------------------------------------------*\\r
6525  *\r
6526  * About box dialog functions\r
6527  *\r
6528 \*---------------------------------------------------------------------------*/\r
6529 \r
6530 /* Process messages for "About" dialog box */\r
6531 LRESULT CALLBACK\r
6532 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6533 {\r
6534   switch (message) {\r
6535   case WM_INITDIALOG: /* message: initialize dialog box */\r
6536     /* Center the dialog over the application window */\r
6537     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6538     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6539     return (TRUE);\r
6540 \r
6541   case WM_COMMAND: /* message: received a command */\r
6542     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6543         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6544       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6545       return (TRUE);\r
6546     }\r
6547     break;\r
6548   }\r
6549   return (FALSE);\r
6550 }\r
6551 \r
6552 /*---------------------------------------------------------------------------*\\r
6553  *\r
6554  * Comment Dialog functions\r
6555  *\r
6556 \*---------------------------------------------------------------------------*/\r
6557 \r
6558 LRESULT CALLBACK\r
6559 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6560 {\r
6561   static HANDLE hwndText = NULL;\r
6562   int len, newSizeX, newSizeY, flags;\r
6563   static int sizeX, sizeY;\r
6564   char *str;\r
6565   RECT rect;\r
6566   MINMAXINFO *mmi;\r
6567 \r
6568   switch (message) {\r
6569   case WM_INITDIALOG: /* message: initialize dialog box */\r
6570     /* Initialize the dialog items */\r
6571     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6572     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6573     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6574     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6575     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6576     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6577     SetWindowText(hDlg, commentTitle);\r
6578     if (editComment) {\r
6579       SetFocus(hwndText);\r
6580     } else {\r
6581       SetFocus(GetDlgItem(hDlg, IDOK));\r
6582     }\r
6583     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6584                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6585                 MAKELPARAM(FALSE, 0));\r
6586     /* Size and position the dialog */\r
6587     if (!commentDialog) {\r
6588       commentDialog = hDlg;\r
6589       flags = SWP_NOZORDER;\r
6590       GetClientRect(hDlg, &rect);\r
6591       sizeX = rect.right;\r
6592       sizeY = rect.bottom;\r
6593       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6594           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6595         WINDOWPLACEMENT wp;\r
6596         EnsureOnScreen(&commentX, &commentY);\r
6597         wp.length = sizeof(WINDOWPLACEMENT);\r
6598         wp.flags = 0;\r
6599         wp.showCmd = SW_SHOW;\r
6600         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6601         wp.rcNormalPosition.left = commentX;\r
6602         wp.rcNormalPosition.right = commentX + commentW;\r
6603         wp.rcNormalPosition.top = commentY;\r
6604         wp.rcNormalPosition.bottom = commentY + commentH;\r
6605         SetWindowPlacement(hDlg, &wp);\r
6606 \r
6607         GetClientRect(hDlg, &rect);\r
6608         newSizeX = rect.right;\r
6609         newSizeY = rect.bottom;\r
6610         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6611                               newSizeX, newSizeY);\r
6612         sizeX = newSizeX;\r
6613         sizeY = newSizeY;\r
6614       }\r
6615     }\r
6616     return FALSE;\r
6617 \r
6618   case WM_COMMAND: /* message: received a command */\r
6619     switch (LOWORD(wParam)) {\r
6620     case IDOK:\r
6621       if (editComment) {\r
6622         char *p, *q;\r
6623         /* Read changed options from the dialog box */\r
6624         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6625         len = GetWindowTextLength(hwndText);\r
6626         str = (char *) malloc(len + 1);\r
6627         GetWindowText(hwndText, str, len + 1);\r
6628         p = q = str;\r
6629         while (*q) {\r
6630           if (*q == '\r')\r
6631             q++;\r
6632           else\r
6633             *p++ = *q++;\r
6634         }\r
6635         *p = NULLCHAR;\r
6636         ReplaceComment(commentIndex, str);\r
6637         free(str);\r
6638       }\r
6639       CommentPopDown();\r
6640       return TRUE;\r
6641 \r
6642     case IDCANCEL:\r
6643     case OPT_CancelComment:\r
6644       CommentPopDown();\r
6645       return TRUE;\r
6646 \r
6647     case OPT_ClearComment:\r
6648       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6649       break;\r
6650 \r
6651     case OPT_EditComment:\r
6652       EditCommentEvent();\r
6653       return TRUE;\r
6654 \r
6655     default:\r
6656       break;\r
6657     }\r
6658     break;\r
6659 \r
6660   case WM_SIZE:\r
6661     newSizeX = LOWORD(lParam);\r
6662     newSizeY = HIWORD(lParam);\r
6663     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6664     sizeX = newSizeX;\r
6665     sizeY = newSizeY;\r
6666     break;\r
6667 \r
6668   case WM_GETMINMAXINFO:\r
6669     /* Prevent resizing window too small */\r
6670     mmi = (MINMAXINFO *) lParam;\r
6671     mmi->ptMinTrackSize.x = 100;\r
6672     mmi->ptMinTrackSize.y = 100;\r
6673     break;\r
6674   }\r
6675   return FALSE;\r
6676 }\r
6677 \r
6678 VOID\r
6679 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6680 {\r
6681   FARPROC lpProc;\r
6682   char *p, *q;\r
6683 \r
6684   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6685 \r
6686   if (str == NULL) str = "";\r
6687   p = (char *) malloc(2 * strlen(str) + 2);\r
6688   q = p;\r
6689   while (*str) {\r
6690     if (*str == '\n') *q++ = '\r';\r
6691     *q++ = *str++;\r
6692   }\r
6693   *q = NULLCHAR;\r
6694   if (commentText != NULL) free(commentText);\r
6695 \r
6696   commentIndex = index;\r
6697   commentTitle = title;\r
6698   commentText = p;\r
6699   editComment = edit;\r
6700 \r
6701   if (commentDialog) {\r
6702     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6703     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6704   } else {\r
6705     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6706     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6707                  hwndMain, (DLGPROC)lpProc);\r
6708     FreeProcInstance(lpProc);\r
6709   }\r
6710   commentDialogUp = TRUE;\r
6711 }\r
6712 \r
6713 \r
6714 /*---------------------------------------------------------------------------*\\r
6715  *\r
6716  * Type-in move dialog functions\r
6717  * \r
6718 \*---------------------------------------------------------------------------*/\r
6719 \r
6720 LRESULT CALLBACK\r
6721 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6722 {\r
6723   char move[MSG_SIZ];\r
6724   HWND hInput;\r
6725   ChessMove moveType;\r
6726   int fromX, fromY, toX, toY;\r
6727   char promoChar;\r
6728 \r
6729   switch (message) {\r
6730   case WM_INITDIALOG:\r
6731     move[0] = (char) lParam;\r
6732     move[1] = NULLCHAR;\r
6733     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6734     hInput = GetDlgItem(hDlg, OPT_Move);\r
6735     SetWindowText(hInput, move);\r
6736     SetFocus(hInput);\r
6737     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6738     return FALSE;\r
6739 \r
6740   case WM_COMMAND:\r
6741     switch (LOWORD(wParam)) {\r
6742     case IDOK:\r
6743       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6744         gameMode != Training) {\r
6745         DisplayMoveError("Displayed move is not current");\r
6746       } else {\r
6747         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6748         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6749           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6750           if (gameMode != Training)\r
6751               forwardMostMove = currentMove;\r
6752           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6753         } else {\r
6754           DisplayMoveError("Could not parse move");\r
6755         }\r
6756       }\r
6757       EndDialog(hDlg, TRUE);\r
6758       return TRUE;\r
6759     case IDCANCEL:\r
6760       EndDialog(hDlg, FALSE);\r
6761       return TRUE;\r
6762     default:\r
6763       break;\r
6764     }\r
6765     break;\r
6766   }\r
6767   return FALSE;\r
6768 }\r
6769 \r
6770 VOID\r
6771 PopUpMoveDialog(char firstchar)\r
6772 {\r
6773     FARPROC lpProc;\r
6774     \r
6775     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6776         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6777         gameMode == AnalyzeMode || gameMode == EditGame || \r
6778         gameMode == EditPosition || gameMode == IcsExamining ||\r
6779         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6780         gameMode == Training) {\r
6781       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6782       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6783         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6784       FreeProcInstance(lpProc);\r
6785     }\r
6786 }\r
6787 \r
6788 /*---------------------------------------------------------------------------*\\r
6789  *\r
6790  *  Error dialogs\r
6791  * \r
6792 \*---------------------------------------------------------------------------*/\r
6793 \r
6794 /* Nonmodal error box */\r
6795 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6796                              WPARAM wParam, LPARAM lParam);\r
6797 \r
6798 VOID\r
6799 ErrorPopUp(char *title, char *content)\r
6800 {\r
6801   FARPROC lpProc;\r
6802   char *p, *q;\r
6803   BOOLEAN modal = hwndMain == NULL;\r
6804 \r
6805   p = content;\r
6806   q = errorMessage;\r
6807   while (*p) {\r
6808     if (*p == '\n') {\r
6809       if (modal) {\r
6810         *q++ = ' ';\r
6811         p++;\r
6812       } else {\r
6813         *q++ = '\r';\r
6814         *q++ = *p++;\r
6815       }\r
6816     } else {\r
6817       *q++ = *p++;\r
6818     }\r
6819   }\r
6820   *q = NULLCHAR;\r
6821   strncpy(errorTitle, title, sizeof(errorTitle));\r
6822   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6823   \r
6824   if (modal) {\r
6825     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6826   } else {\r
6827     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6828     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6829                  hwndMain, (DLGPROC)lpProc);\r
6830     FreeProcInstance(lpProc);\r
6831   }\r
6832 }\r
6833 \r
6834 VOID\r
6835 ErrorPopDown()\r
6836 {\r
6837   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6838   if (errorDialog == NULL) return;\r
6839   DestroyWindow(errorDialog);\r
6840   errorDialog = NULL;\r
6841 }\r
6842 \r
6843 LRESULT CALLBACK\r
6844 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6845 {\r
6846   HANDLE hwndText;\r
6847   RECT rChild;\r
6848 \r
6849   switch (message) {\r
6850   case WM_INITDIALOG:\r
6851     GetWindowRect(hDlg, &rChild);\r
6852 \r
6853     /*\r
6854     SetWindowPos(hDlg, NULL, rChild.left,\r
6855       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6856       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6857     */\r
6858 \r
6859     /* \r
6860         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6861         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6862         and it doesn't work when you resize the dialog.\r
6863         For now, just give it a default position.\r
6864     */\r
6865     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6866 \r
6867     errorDialog = hDlg;\r
6868     SetWindowText(hDlg, errorTitle);\r
6869     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6870     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6871     return FALSE;\r
6872 \r
6873   case WM_COMMAND:\r
6874     switch (LOWORD(wParam)) {\r
6875     case IDOK:\r
6876     case IDCANCEL:\r
6877       if (errorDialog == hDlg) errorDialog = NULL;\r
6878       DestroyWindow(hDlg);\r
6879       return TRUE;\r
6880 \r
6881     default:\r
6882       break;\r
6883     }\r
6884     break;\r
6885   }\r
6886   return FALSE;\r
6887 }\r
6888 \r
6889 #ifdef GOTHIC\r
6890 HWND gothicDialog = NULL;\r
6891 \r
6892 LRESULT CALLBACK\r
6893 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6894 {\r
6895   HANDLE hwndText;\r
6896   RECT rChild;\r
6897   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6898 \r
6899   switch (message) {\r
6900   case WM_INITDIALOG:\r
6901     GetWindowRect(hDlg, &rChild);\r
6902 \r
6903     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6904                                                              SWP_NOZORDER);\r
6905 \r
6906     /* \r
6907         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6908         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6909         and it doesn't work when you resize the dialog.\r
6910         For now, just give it a default position.\r
6911     */\r
6912     gothicDialog = hDlg;\r
6913     SetWindowText(hDlg, errorTitle);\r
6914     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6915     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6916     return FALSE;\r
6917 \r
6918   case WM_COMMAND:\r
6919     switch (LOWORD(wParam)) {\r
6920     case IDOK:\r
6921     case IDCANCEL:\r
6922       if (errorDialog == hDlg) errorDialog = NULL;\r
6923       DestroyWindow(hDlg);\r
6924       return TRUE;\r
6925 \r
6926     default:\r
6927       break;\r
6928     }\r
6929     break;\r
6930   }\r
6931   return FALSE;\r
6932 }\r
6933 \r
6934 VOID\r
6935 GothicPopUp(char *title, VariantClass variant)\r
6936 {\r
6937   FARPROC lpProc;\r
6938   char *p, *q;\r
6939   BOOLEAN modal = hwndMain == NULL;\r
6940   static char *lastTitle;\r
6941 \r
6942   strncpy(errorTitle, title, sizeof(errorTitle));\r
6943   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6944 \r
6945   if(lastTitle != title && gothicDialog != NULL) {\r
6946     DestroyWindow(gothicDialog);\r
6947     gothicDialog = NULL;\r
6948   }\r
6949   if(variant != VariantNormal && gothicDialog == NULL) {\r
6950     title = lastTitle;\r
6951     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6952     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6953                  hwndMain, (DLGPROC)lpProc);\r
6954     FreeProcInstance(lpProc);\r
6955   }\r
6956 }\r
6957 #endif\r
6958 \r
6959 /*---------------------------------------------------------------------------*\\r
6960  *\r
6961  *  Ics Interaction console functions\r
6962  *\r
6963 \*---------------------------------------------------------------------------*/\r
6964 \r
6965 #define HISTORY_SIZE 64\r
6966 static char *history[HISTORY_SIZE];\r
6967 int histIn = 0, histP = 0;\r
6968 \r
6969 VOID\r
6970 SaveInHistory(char *cmd)\r
6971 {\r
6972   if (history[histIn] != NULL) {\r
6973     free(history[histIn]);\r
6974     history[histIn] = NULL;\r
6975   }\r
6976   if (*cmd == NULLCHAR) return;\r
6977   history[histIn] = StrSave(cmd);\r
6978   histIn = (histIn + 1) % HISTORY_SIZE;\r
6979   if (history[histIn] != NULL) {\r
6980     free(history[histIn]);\r
6981     history[histIn] = NULL;\r
6982   }\r
6983   histP = histIn;\r
6984 }\r
6985 \r
6986 char *\r
6987 PrevInHistory(char *cmd)\r
6988 {\r
6989   int newhp;\r
6990   if (histP == histIn) {\r
6991     if (history[histIn] != NULL) free(history[histIn]);\r
6992     history[histIn] = StrSave(cmd);\r
6993   }\r
6994   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6995   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6996   histP = newhp;\r
6997   return history[histP];\r
6998 }\r
6999 \r
7000 char *\r
7001 NextInHistory()\r
7002 {\r
7003   if (histP == histIn) return NULL;\r
7004   histP = (histP + 1) % HISTORY_SIZE;\r
7005   return history[histP];\r
7006 }\r
7007 \r
7008 typedef struct {\r
7009   char *item;\r
7010   char *command;\r
7011   BOOLEAN getname;\r
7012   BOOLEAN immediate;\r
7013 } IcsTextMenuEntry;\r
7014 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7015 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7016 \r
7017 void\r
7018 ParseIcsTextMenu(char *icsTextMenuString)\r
7019 {\r
7020   int flags = 0;\r
7021   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7022   char *p = icsTextMenuString;\r
7023   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7024     free(e->item);\r
7025     e->item = NULL;\r
7026     if (e->command != NULL) {\r
7027       free(e->command);\r
7028       e->command = NULL;\r
7029     }\r
7030     e++;\r
7031   }\r
7032   e = icsTextMenuEntry;\r
7033   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7034     if (*p == ';' || *p == '\n') {\r
7035       e->item = strdup("-");\r
7036       e->command = NULL;\r
7037       p++;\r
7038     } else if (*p == '-') {\r
7039       e->item = strdup("-");\r
7040       e->command = NULL;\r
7041       p++;\r
7042       if (*p) p++;\r
7043     } else {\r
7044       char *q, *r, *s, *t;\r
7045       char c;\r
7046       q = strchr(p, ',');\r
7047       if (q == NULL) break;\r
7048       *q = NULLCHAR;\r
7049       r = strchr(q + 1, ',');\r
7050       if (r == NULL) break;\r
7051       *r = NULLCHAR;\r
7052       s = strchr(r + 1, ',');\r
7053       if (s == NULL) break;\r
7054       *s = NULLCHAR;\r
7055       c = ';';\r
7056       t = strchr(s + 1, c);\r
7057       if (t == NULL) {\r
7058         c = '\n';\r
7059         t = strchr(s + 1, c);\r
7060       }\r
7061       if (t != NULL) *t = NULLCHAR;\r
7062       e->item = strdup(p);\r
7063       e->command = strdup(q + 1);\r
7064       e->getname = *(r + 1) != '0';\r
7065       e->immediate = *(s + 1) != '0';\r
7066       *q = ',';\r
7067       *r = ',';\r
7068       *s = ',';\r
7069       if (t == NULL) break;\r
7070       *t = c;\r
7071       p = t + 1;\r
7072     }\r
7073     e++;\r
7074   } \r
7075 }\r
7076 \r
7077 HMENU\r
7078 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7079 {\r
7080   HMENU hmenu, h;\r
7081   int i = 0;\r
7082   hmenu = LoadMenu(hInst, "TextMenu");\r
7083   h = GetSubMenu(hmenu, 0);\r
7084   while (e->item) {\r
7085     if (strcmp(e->item, "-") == 0) {\r
7086       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7087     } else {\r
7088       if (e->item[0] == '|') {\r
7089         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7090                    IDM_CommandX + i, &e->item[1]);\r
7091       } else {\r
7092         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7093       }\r
7094     }\r
7095     e++;\r
7096     i++;\r
7097   } \r
7098   return hmenu;\r
7099 }\r
7100 \r
7101 WNDPROC consoleTextWindowProc;\r
7102 \r
7103 void\r
7104 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7105 {\r
7106   char buf[MSG_SIZ], name[MSG_SIZ];\r
7107   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7108   CHARRANGE sel;\r
7109 \r
7110   if (!getname) {\r
7111     SetWindowText(hInput, command);\r
7112     if (immediate) {\r
7113       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7114     } else {\r
7115       sel.cpMin = 999999;\r
7116       sel.cpMax = 999999;\r
7117       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7118       SetFocus(hInput);\r
7119     }\r
7120     return;\r
7121   }    \r
7122   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7123   if (sel.cpMin == sel.cpMax) {\r
7124     /* Expand to surrounding word */\r
7125     TEXTRANGE tr;\r
7126     do {\r
7127       tr.chrg.cpMax = sel.cpMin;\r
7128       tr.chrg.cpMin = --sel.cpMin;\r
7129       if (sel.cpMin < 0) break;\r
7130       tr.lpstrText = name;\r
7131       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7132     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7133     sel.cpMin++;\r
7134 \r
7135     do {\r
7136       tr.chrg.cpMin = sel.cpMax;\r
7137       tr.chrg.cpMax = ++sel.cpMax;\r
7138       tr.lpstrText = name;\r
7139       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7140     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7141     sel.cpMax--;\r
7142 \r
7143     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7144       MessageBeep(MB_ICONEXCLAMATION);\r
7145       return;\r
7146     }\r
7147     tr.chrg = sel;\r
7148     tr.lpstrText = name;\r
7149     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7150   } else {\r
7151     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7152       MessageBeep(MB_ICONEXCLAMATION);\r
7153       return;\r
7154     }\r
7155     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7156   }\r
7157   if (immediate) {\r
7158     sprintf(buf, "%s %s", command, name);\r
7159     SetWindowText(hInput, buf);\r
7160     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7161   } else {\r
7162     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7163     SetWindowText(hInput, buf);\r
7164     sel.cpMin = 999999;\r
7165     sel.cpMax = 999999;\r
7166     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7167     SetFocus(hInput);\r
7168   }\r
7169 }\r
7170 \r
7171 LRESULT CALLBACK \r
7172 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7173 {\r
7174   HWND hInput;\r
7175   CHARRANGE sel;\r
7176 \r
7177   switch (message) {\r
7178   case WM_KEYDOWN:\r
7179     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7180     switch (wParam) {\r
7181     case VK_PRIOR:\r
7182       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7183       return 0;\r
7184     case VK_NEXT:\r
7185       sel.cpMin = 999999;\r
7186       sel.cpMax = 999999;\r
7187       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7188       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7189       return 0;\r
7190     }\r
7191     break;\r
7192   case WM_CHAR:\r
7193     if (wParam == '\t') {\r
7194       if (GetKeyState(VK_SHIFT) < 0) {\r
7195         /* shifted */\r
7196         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7197         if (buttonDesc[0].hwnd) {\r
7198           SetFocus(buttonDesc[0].hwnd);\r
7199         } else {\r
7200           SetFocus(hwndMain);\r
7201         }\r
7202       } else {\r
7203         /* unshifted */\r
7204         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7205       }\r
7206     } else {\r
7207       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7208       SetFocus(hInput);\r
7209       SendMessage(hInput, message, wParam, lParam);\r
7210     }\r
7211     return 0;\r
7212   case WM_PASTE:\r
7213     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7214     SetFocus(hInput);\r
7215     return SendMessage(hInput, message, wParam, lParam);\r
7216   case WM_MBUTTONDOWN:\r
7217     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7218   case WM_RBUTTONDOWN:\r
7219     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7220       /* Move selection here if it was empty */\r
7221       POINT pt;\r
7222       pt.x = LOWORD(lParam);\r
7223       pt.y = HIWORD(lParam);\r
7224       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7225       if (sel.cpMin == sel.cpMax) {\r
7226         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7227         sel.cpMax = sel.cpMin;\r
7228         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7229       }\r
7230       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7231     }\r
7232     return 0;\r
7233   case WM_RBUTTONUP:\r
7234     if (GetKeyState(VK_SHIFT) & ~1) {\r
7235       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7236         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7237     } else {\r
7238       POINT pt;\r
7239       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7240       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7241       if (sel.cpMin == sel.cpMax) {\r
7242         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7243         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7244       }\r
7245       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7246         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7247       }\r
7248       pt.x = LOWORD(lParam);\r
7249       pt.y = HIWORD(lParam);\r
7250       MenuPopup(hwnd, pt, hmenu, -1);\r
7251     }\r
7252     return 0;\r
7253   case WM_COMMAND:\r
7254     switch (LOWORD(wParam)) {\r
7255     case IDM_QuickPaste:\r
7256       {\r
7257         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7258         if (sel.cpMin == sel.cpMax) {\r
7259           MessageBeep(MB_ICONEXCLAMATION);\r
7260           return 0;\r
7261         }\r
7262         SendMessage(hwnd, WM_COPY, 0, 0);\r
7263         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7264         SendMessage(hInput, WM_PASTE, 0, 0);\r
7265         SetFocus(hInput);\r
7266         return 0;\r
7267       }\r
7268     case IDM_Cut:\r
7269       SendMessage(hwnd, WM_CUT, 0, 0);\r
7270       return 0;\r
7271     case IDM_Paste:\r
7272       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7273       return 0;\r
7274     case IDM_Copy:\r
7275       SendMessage(hwnd, WM_COPY, 0, 0);\r
7276       return 0;\r
7277     default:\r
7278       {\r
7279         int i = LOWORD(wParam) - IDM_CommandX;\r
7280         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7281             icsTextMenuEntry[i].command != NULL) {\r
7282           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7283                    icsTextMenuEntry[i].getname,\r
7284                    icsTextMenuEntry[i].immediate);\r
7285           return 0;\r
7286         }\r
7287       }\r
7288       break;\r
7289     }\r
7290     break;\r
7291   }\r
7292   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7293 }\r
7294 \r
7295 WNDPROC consoleInputWindowProc;\r
7296 \r
7297 LRESULT CALLBACK\r
7298 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7299 {\r
7300   char buf[MSG_SIZ];\r
7301   char *p;\r
7302   static BOOL sendNextChar = FALSE;\r
7303   static BOOL quoteNextChar = FALSE;\r
7304   InputSource *is = consoleInputSource;\r
7305   CHARFORMAT cf;\r
7306   CHARRANGE sel;\r
7307 \r
7308   switch (message) {\r
7309   case WM_CHAR:\r
7310     if (!appData.localLineEditing || sendNextChar) {\r
7311       is->buf[0] = (CHAR) wParam;\r
7312       is->count = 1;\r
7313       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7314       sendNextChar = FALSE;\r
7315       return 0;\r
7316     }\r
7317     if (quoteNextChar) {\r
7318       buf[0] = (char) wParam;\r
7319       buf[1] = NULLCHAR;\r
7320       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7321       quoteNextChar = FALSE;\r
7322       return 0;\r
7323     }\r
7324     switch (wParam) {\r
7325     case '\r':   /* Enter key */\r
7326       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7327       if (consoleEcho) SaveInHistory(is->buf);\r
7328       is->buf[is->count++] = '\n';\r
7329       is->buf[is->count] = NULLCHAR;\r
7330       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7331       if (consoleEcho) {\r
7332         ConsoleOutput(is->buf, is->count, TRUE);\r
7333       } else if (appData.localLineEditing) {\r
7334         ConsoleOutput("\n", 1, TRUE);\r
7335       }\r
7336       /* fall thru */\r
7337     case '\033': /* Escape key */\r
7338       SetWindowText(hwnd, "");\r
7339       cf.cbSize = sizeof(CHARFORMAT);\r
7340       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7341       if (consoleEcho) {\r
7342         cf.crTextColor = textAttribs[ColorNormal].color;\r
7343       } else {\r
7344         cf.crTextColor = COLOR_ECHOOFF;\r
7345       }\r
7346       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7347       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7348       return 0;\r
7349     case '\t':   /* Tab key */\r
7350       if (GetKeyState(VK_SHIFT) < 0) {\r
7351         /* shifted */\r
7352         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7353       } else {\r
7354         /* unshifted */\r
7355         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7356         if (buttonDesc[0].hwnd) {\r
7357           SetFocus(buttonDesc[0].hwnd);\r
7358         } else {\r
7359           SetFocus(hwndMain);\r
7360         }\r
7361       }\r
7362       return 0;\r
7363     case '\023': /* Ctrl+S */\r
7364       sendNextChar = TRUE;\r
7365       return 0;\r
7366     case '\021': /* Ctrl+Q */\r
7367       quoteNextChar = TRUE;\r
7368       return 0;\r
7369     default:\r
7370       break;\r
7371     }\r
7372     break;\r
7373   case WM_KEYDOWN:\r
7374     switch (wParam) {\r
7375     case VK_UP:\r
7376       GetWindowText(hwnd, buf, MSG_SIZ);\r
7377       p = PrevInHistory(buf);\r
7378       if (p != NULL) {\r
7379         SetWindowText(hwnd, p);\r
7380         sel.cpMin = 999999;\r
7381         sel.cpMax = 999999;\r
7382         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7383         return 0;\r
7384       }\r
7385       break;\r
7386     case VK_DOWN:\r
7387       p = NextInHistory();\r
7388       if (p != NULL) {\r
7389         SetWindowText(hwnd, p);\r
7390         sel.cpMin = 999999;\r
7391         sel.cpMax = 999999;\r
7392         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7393         return 0;\r
7394       }\r
7395       break;\r
7396     case VK_HOME:\r
7397     case VK_END:\r
7398       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7399       /* fall thru */\r
7400     case VK_PRIOR:\r
7401     case VK_NEXT:\r
7402       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7403       return 0;\r
7404     }\r
7405     break;\r
7406   case WM_MBUTTONDOWN:\r
7407     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7408       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7409     break;\r
7410   case WM_RBUTTONUP:\r
7411     if (GetKeyState(VK_SHIFT) & ~1) {\r
7412       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7413         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7414     } else {\r
7415       POINT pt;\r
7416       HMENU hmenu;\r
7417       hmenu = LoadMenu(hInst, "InputMenu");\r
7418       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7419       if (sel.cpMin == sel.cpMax) {\r
7420         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7421         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7422       }\r
7423       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7424         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7425       }\r
7426       pt.x = LOWORD(lParam);\r
7427       pt.y = HIWORD(lParam);\r
7428       MenuPopup(hwnd, pt, hmenu, -1);\r
7429     }\r
7430     return 0;\r
7431   case WM_COMMAND:\r
7432     switch (LOWORD(wParam)) { \r
7433     case IDM_Undo:\r
7434       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7435       return 0;\r
7436     case IDM_SelectAll:\r
7437       sel.cpMin = 0;\r
7438       sel.cpMax = -1; /*999999?*/\r
7439       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7440       return 0;\r
7441     case IDM_Cut:\r
7442       SendMessage(hwnd, WM_CUT, 0, 0);\r
7443       return 0;\r
7444     case IDM_Paste:\r
7445       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7446       return 0;\r
7447     case IDM_Copy:\r
7448       SendMessage(hwnd, WM_COPY, 0, 0);\r
7449       return 0;\r
7450     }\r
7451     break;\r
7452   }\r
7453   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7454 }\r
7455 \r
7456 #define CO_MAX  100000\r
7457 #define CO_TRIM   1000\r
7458 \r
7459 LRESULT CALLBACK\r
7460 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7461 {\r
7462   static SnapData sd;\r
7463   static HWND hText, hInput, hFocus;\r
7464   InputSource *is = consoleInputSource;\r
7465   RECT rect;\r
7466   static int sizeX, sizeY;\r
7467   int newSizeX, newSizeY;\r
7468   MINMAXINFO *mmi;\r
7469 \r
7470   switch (message) {\r
7471   case WM_INITDIALOG: /* message: initialize dialog box */\r
7472     hwndConsole = hDlg;\r
7473     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7474     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7475     SetFocus(hInput);\r
7476     consoleTextWindowProc = (WNDPROC)\r
7477       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7478     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7479     consoleInputWindowProc = (WNDPROC)\r
7480       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7481     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7482     Colorize(ColorNormal, TRUE);\r
7483     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7484     ChangedConsoleFont();\r
7485     GetClientRect(hDlg, &rect);\r
7486     sizeX = rect.right;\r
7487     sizeY = rect.bottom;\r
7488     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7489         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7490       WINDOWPLACEMENT wp;\r
7491       EnsureOnScreen(&consoleX, &consoleY);\r
7492       wp.length = sizeof(WINDOWPLACEMENT);\r
7493       wp.flags = 0;\r
7494       wp.showCmd = SW_SHOW;\r
7495       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7496       wp.rcNormalPosition.left = consoleX;\r
7497       wp.rcNormalPosition.right = consoleX + consoleW;\r
7498       wp.rcNormalPosition.top = consoleY;\r
7499       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7500       SetWindowPlacement(hDlg, &wp);\r
7501     }\r
7502     return FALSE;\r
7503 \r
7504   case WM_SETFOCUS:\r
7505     SetFocus(hInput);\r
7506     return 0;\r
7507 \r
7508   case WM_CLOSE:\r
7509     ExitEvent(0);\r
7510     /* not reached */\r
7511     break;\r
7512 \r
7513   case WM_SIZE:\r
7514     if (IsIconic(hDlg)) break;\r
7515     newSizeX = LOWORD(lParam);\r
7516     newSizeY = HIWORD(lParam);\r
7517     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7518       RECT rectText, rectInput;\r
7519       POINT pt;\r
7520       int newTextHeight, newTextWidth;\r
7521       GetWindowRect(hText, &rectText);\r
7522       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7523       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7524       if (newTextHeight < 0) {\r
7525         newSizeY += -newTextHeight;\r
7526         newTextHeight = 0;\r
7527       }\r
7528       SetWindowPos(hText, NULL, 0, 0,\r
7529         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7530       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7531       pt.x = rectInput.left;\r
7532       pt.y = rectInput.top + newSizeY - sizeY;\r
7533       ScreenToClient(hDlg, &pt);\r
7534       SetWindowPos(hInput, NULL, \r
7535         pt.x, pt.y, /* needs client coords */   \r
7536         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7537         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7538     }\r
7539     sizeX = newSizeX;\r
7540     sizeY = newSizeY;\r
7541     break;\r
7542 \r
7543   case WM_GETMINMAXINFO:\r
7544     /* Prevent resizing window too small */\r
7545     mmi = (MINMAXINFO *) lParam;\r
7546     mmi->ptMinTrackSize.x = 100;\r
7547     mmi->ptMinTrackSize.y = 100;\r
7548     break;\r
7549 \r
7550   /* [AS] Snapping */\r
7551   case WM_ENTERSIZEMOVE:\r
7552     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7553 \r
7554   case WM_SIZING:\r
7555     return OnSizing( &sd, hDlg, wParam, lParam );\r
7556 \r
7557   case WM_MOVING:\r
7558     return OnMoving( &sd, hDlg, wParam, lParam );\r
7559 \r
7560   case WM_EXITSIZEMOVE:\r
7561     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7562   }\r
7563 \r
7564   return DefWindowProc(hDlg, message, wParam, lParam);\r
7565 }\r
7566 \r
7567 \r
7568 VOID\r
7569 ConsoleCreate()\r
7570 {\r
7571   HWND hCons;\r
7572   if (hwndConsole) return;\r
7573   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7574   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7575 }\r
7576 \r
7577 \r
7578 VOID\r
7579 ConsoleOutput(char* data, int length, int forceVisible)\r
7580 {\r
7581   HWND hText;\r
7582   int trim, exlen;\r
7583   char *p, *q;\r
7584   char buf[CO_MAX+1];\r
7585   POINT pEnd;\r
7586   RECT rect;\r
7587   static int delayLF = 0;\r
7588   CHARRANGE savesel, sel;\r
7589 \r
7590   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7591   p = data;\r
7592   q = buf;\r
7593   if (delayLF) {\r
7594     *q++ = '\r';\r
7595     *q++ = '\n';\r
7596     delayLF = 0;\r
7597   }\r
7598   while (length--) {\r
7599     if (*p == '\n') {\r
7600       if (*++p) {\r
7601         *q++ = '\r';\r
7602         *q++ = '\n';\r
7603       } else {\r
7604         delayLF = 1;\r
7605       }\r
7606     } else if (*p == '\007') {\r
7607        MyPlaySound(&sounds[(int)SoundBell]);\r
7608        p++;\r
7609     } else {\r
7610       *q++ = *p++;\r
7611     }\r
7612   }\r
7613   *q = NULLCHAR;\r
7614   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7615   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7616   /* Save current selection */\r
7617   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7618   exlen = GetWindowTextLength(hText);\r
7619   /* Find out whether current end of text is visible */\r
7620   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7621   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7622   /* Trim existing text if it's too long */\r
7623   if (exlen + (q - buf) > CO_MAX) {\r
7624     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7625     sel.cpMin = 0;\r
7626     sel.cpMax = trim;\r
7627     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7628     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7629     exlen -= trim;\r
7630     savesel.cpMin -= trim;\r
7631     savesel.cpMax -= trim;\r
7632     if (exlen < 0) exlen = 0;\r
7633     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7634     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7635   }\r
7636   /* Append the new text */\r
7637   sel.cpMin = exlen;\r
7638   sel.cpMax = exlen;\r
7639   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7640   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7641   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7642   if (forceVisible || exlen == 0 ||\r
7643       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7644        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7645     /* Scroll to make new end of text visible if old end of text\r
7646        was visible or new text is an echo of user typein */\r
7647     sel.cpMin = 9999999;\r
7648     sel.cpMax = 9999999;\r
7649     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7650     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7651     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7652     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7653   }\r
7654   if (savesel.cpMax == exlen || forceVisible) {\r
7655     /* Move insert point to new end of text if it was at the old\r
7656        end of text or if the new text is an echo of user typein */\r
7657     sel.cpMin = 9999999;\r
7658     sel.cpMax = 9999999;\r
7659     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7660   } else {\r
7661     /* Restore previous selection */\r
7662     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7663   }\r
7664   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7665 }\r
7666 \r
7667 /*---------*/\r
7668 \r
7669 \r
7670 void\r
7671 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7672 {\r
7673   char buf[100];\r
7674   char *str;\r
7675   COLORREF oldFg, oldBg;\r
7676   HFONT oldFont;\r
7677   RECT rect;\r
7678 \r
7679   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7680 \r
7681   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7682   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7683   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7684 \r
7685   rect.left = x;\r
7686   rect.right = x + squareSize;\r
7687   rect.top  = y;\r
7688   rect.bottom = y + squareSize;\r
7689   str = buf;\r
7690 \r
7691   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7692                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7693              y, ETO_CLIPPED|ETO_OPAQUE,\r
7694              &rect, str, strlen(str), NULL);\r
7695 \r
7696   (void) SetTextColor(hdc, oldFg);\r
7697   (void) SetBkColor(hdc, oldBg);\r
7698   (void) SelectObject(hdc, oldFont);\r
7699 }\r
7700 \r
7701 void\r
7702 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7703               RECT *rect, char *color, char *flagFell)\r
7704 {\r
7705   char buf[100];\r
7706   char *str;\r
7707   COLORREF oldFg, oldBg;\r
7708   HFONT oldFont;\r
7709 \r
7710   if (appData.clockMode) {\r
7711     if (tinyLayout)\r
7712       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7713     else\r
7714       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7715     str = buf;\r
7716   } else {\r
7717     str = color;\r
7718   }\r
7719 \r
7720   if (highlight) {\r
7721     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7722     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7723   } else {\r
7724     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7725     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7726   }\r
7727   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7728 \r
7729   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7730              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7731              rect, str, strlen(str), NULL);\r
7732 \r
7733   (void) SetTextColor(hdc, oldFg);\r
7734   (void) SetBkColor(hdc, oldBg);\r
7735   (void) SelectObject(hdc, oldFont);\r
7736 }\r
7737 \r
7738 \r
7739 int\r
7740 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7741            OVERLAPPED *ovl)\r
7742 {\r
7743   int ok, err;\r
7744 \r
7745   /* [AS]  */\r
7746   if( count <= 0 ) {\r
7747     if (appData.debugMode) {\r
7748       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7749     }\r
7750 \r
7751     return ERROR_INVALID_USER_BUFFER;\r
7752   }\r
7753 \r
7754   ResetEvent(ovl->hEvent);\r
7755   ovl->Offset = ovl->OffsetHigh = 0;\r
7756   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7757   if (ok) {\r
7758     err = NO_ERROR;\r
7759   } else {\r
7760     err = GetLastError();\r
7761     if (err == ERROR_IO_PENDING) {\r
7762       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7763       if (ok)\r
7764         err = NO_ERROR;\r
7765       else\r
7766         err = GetLastError();\r
7767     }\r
7768   }\r
7769   return err;\r
7770 }\r
7771 \r
7772 int\r
7773 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7774             OVERLAPPED *ovl)\r
7775 {\r
7776   int ok, err;\r
7777 \r
7778   ResetEvent(ovl->hEvent);\r
7779   ovl->Offset = ovl->OffsetHigh = 0;\r
7780   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7781   if (ok) {\r
7782     err = NO_ERROR;\r
7783   } else {\r
7784     err = GetLastError();\r
7785     if (err == ERROR_IO_PENDING) {\r
7786       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7787       if (ok)\r
7788         err = NO_ERROR;\r
7789       else\r
7790         err = GetLastError();\r
7791     }\r
7792   }\r
7793   return err;\r
7794 }\r
7795 \r
7796 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7797 void CheckForInputBufferFull( InputSource * is )\r
7798 {\r
7799     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7800         /* Look for end of line */\r
7801         char * p = is->buf;\r
7802         \r
7803         while( p < is->next && *p != '\n' ) {\r
7804             p++;\r
7805         }\r
7806 \r
7807         if( p >= is->next ) {\r
7808             if (appData.debugMode) {\r
7809                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7810             }\r
7811 \r
7812             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7813             is->count = (DWORD) -1;\r
7814             is->next = is->buf;\r
7815         }\r
7816     }\r
7817 }\r
7818 \r
7819 DWORD\r
7820 InputThread(LPVOID arg)\r
7821 {\r
7822   InputSource *is;\r
7823   OVERLAPPED ovl;\r
7824 \r
7825   is = (InputSource *) arg;\r
7826   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7827   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7828   while (is->hThread != NULL) {\r
7829     is->error = DoReadFile(is->hFile, is->next,\r
7830                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7831                            &is->count, &ovl);\r
7832     if (is->error == NO_ERROR) {\r
7833       is->next += is->count;\r
7834     } else {\r
7835       if (is->error == ERROR_BROKEN_PIPE) {\r
7836         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7837         is->count = 0;\r
7838       } else {\r
7839         is->count = (DWORD) -1;\r
7840         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7841         break; \r
7842       }\r
7843     }\r
7844 \r
7845     CheckForInputBufferFull( is );\r
7846 \r
7847     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7848 \r
7849     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7850 \r
7851     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7852   }\r
7853 \r
7854   CloseHandle(ovl.hEvent);\r
7855   CloseHandle(is->hFile);\r
7856 \r
7857   if (appData.debugMode) {\r
7858     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7859   }\r
7860 \r
7861   return 0;\r
7862 }\r
7863 \r
7864 \r
7865 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7866 DWORD\r
7867 NonOvlInputThread(LPVOID arg)\r
7868 {\r
7869   InputSource *is;\r
7870   char *p, *q;\r
7871   int i;\r
7872   char prev;\r
7873 \r
7874   is = (InputSource *) arg;\r
7875   while (is->hThread != NULL) {\r
7876     is->error = ReadFile(is->hFile, is->next,\r
7877                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7878                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7879     if (is->error == NO_ERROR) {\r
7880       /* Change CRLF to LF */\r
7881       if (is->next > is->buf) {\r
7882         p = is->next - 1;\r
7883         i = is->count + 1;\r
7884       } else {\r
7885         p = is->next;\r
7886         i = is->count;\r
7887       }\r
7888       q = p;\r
7889       prev = NULLCHAR;\r
7890       while (i > 0) {\r
7891         if (prev == '\r' && *p == '\n') {\r
7892           *(q-1) = '\n';\r
7893           is->count--;\r
7894         } else { \r
7895           *q++ = *p;\r
7896         }\r
7897         prev = *p++;\r
7898         i--;\r
7899       }\r
7900       *q = NULLCHAR;\r
7901       is->next = q;\r
7902     } else {\r
7903       if (is->error == ERROR_BROKEN_PIPE) {\r
7904         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7905         is->count = 0; \r
7906       } else {\r
7907         is->count = (DWORD) -1;\r
7908       }\r
7909     }\r
7910 \r
7911     CheckForInputBufferFull( is );\r
7912 \r
7913     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7914 \r
7915     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7916 \r
7917     if (is->count < 0) break;  /* Quit on error */\r
7918   }\r
7919   CloseHandle(is->hFile);\r
7920   return 0;\r
7921 }\r
7922 \r
7923 DWORD\r
7924 SocketInputThread(LPVOID arg)\r
7925 {\r
7926   InputSource *is;\r
7927 \r
7928   is = (InputSource *) arg;\r
7929   while (is->hThread != NULL) {\r
7930     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7931     if ((int)is->count == SOCKET_ERROR) {\r
7932       is->count = (DWORD) -1;\r
7933       is->error = WSAGetLastError();\r
7934     } else {\r
7935       is->error = NO_ERROR;\r
7936       is->next += is->count;\r
7937       if (is->count == 0 && is->second == is) {\r
7938         /* End of file on stderr; quit with no message */\r
7939         break;\r
7940       }\r
7941     }\r
7942     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7943 \r
7944     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7945 \r
7946     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7947   }\r
7948   return 0;\r
7949 }\r
7950 \r
7951 VOID\r
7952 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7953 {\r
7954   InputSource *is;\r
7955 \r
7956   is = (InputSource *) lParam;\r
7957   if (is->lineByLine) {\r
7958     /* Feed in lines one by one */\r
7959     char *p = is->buf;\r
7960     char *q = p;\r
7961     while (q < is->next) {\r
7962       if (*q++ == '\n') {\r
7963         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7964         p = q;\r
7965       }\r
7966     }\r
7967     \r
7968     /* Move any partial line to the start of the buffer */\r
7969     q = is->buf;\r
7970     while (p < is->next) {\r
7971       *q++ = *p++;\r
7972     }\r
7973     is->next = q;\r
7974 \r
7975     if (is->error != NO_ERROR || is->count == 0) {\r
7976       /* Notify backend of the error.  Note: If there was a partial\r
7977          line at the end, it is not flushed through. */\r
7978       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7979     }\r
7980   } else {\r
7981     /* Feed in the whole chunk of input at once */\r
7982     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7983     is->next = is->buf;\r
7984   }\r
7985 }\r
7986 \r
7987 /*---------------------------------------------------------------------------*\\r
7988  *\r
7989  *  Menu enables. Used when setting various modes.\r
7990  *\r
7991 \*---------------------------------------------------------------------------*/\r
7992 \r
7993 typedef struct {\r
7994   int item;\r
7995   int flags;\r
7996 } Enables;\r
7997 \r
7998 VOID\r
7999 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8000 {\r
8001   while (enab->item > 0) {\r
8002     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8003     enab++;\r
8004   }\r
8005 }\r
8006 \r
8007 Enables gnuEnables[] = {\r
8008   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8018   { -1, -1 }\r
8019 };\r
8020 \r
8021 Enables icsEnables[] = {\r
8022   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8034   { -1, -1 }\r
8035 };\r
8036 \r
8037 #ifdef ZIPPY\r
8038 Enables zippyEnables[] = {\r
8039   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8042   { -1, -1 }\r
8043 };\r
8044 #endif\r
8045 \r
8046 Enables ncpEnables[] = {\r
8047   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8055   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8056   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8062   { -1, -1 }\r
8063 };\r
8064 \r
8065 Enables trainingOnEnables[] = {\r
8066   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8074   { -1, -1 }\r
8075 };\r
8076 \r
8077 Enables trainingOffEnables[] = {\r
8078   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8082   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8083   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8084   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8085   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8086   { -1, -1 }\r
8087 };\r
8088 \r
8089 /* These modify either ncpEnables or gnuEnables */\r
8090 Enables cmailEnables[] = {\r
8091   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8092   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8093   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8094   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8096   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8098   { -1, -1 }\r
8099 };\r
8100 \r
8101 Enables machineThinkingEnables[] = {\r
8102   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8117   { -1, -1 }\r
8118 };\r
8119 \r
8120 Enables userThinkingEnables[] = {\r
8121   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8131   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8132   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8133   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8134   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8135   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8136   { -1, -1 }\r
8137 };\r
8138 \r
8139 /*---------------------------------------------------------------------------*\\r
8140  *\r
8141  *  Front-end interface functions exported by XBoard.\r
8142  *  Functions appear in same order as prototypes in frontend.h.\r
8143  * \r
8144 \*---------------------------------------------------------------------------*/\r
8145 VOID\r
8146 ModeHighlight()\r
8147 {\r
8148   static UINT prevChecked = 0;\r
8149   static int prevPausing = 0;\r
8150   UINT nowChecked;\r
8151 \r
8152   if (pausing != prevPausing) {\r
8153     prevPausing = pausing;\r
8154     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8155                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8156     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8157   }\r
8158 \r
8159   switch (gameMode) {\r
8160   case BeginningOfGame:\r
8161     if (appData.icsActive)\r
8162       nowChecked = IDM_IcsClient;\r
8163     else if (appData.noChessProgram)\r
8164       nowChecked = IDM_EditGame;\r
8165     else\r
8166       nowChecked = IDM_MachineBlack;\r
8167     break;\r
8168   case MachinePlaysBlack:\r
8169     nowChecked = IDM_MachineBlack;\r
8170     break;\r
8171   case MachinePlaysWhite:\r
8172     nowChecked = IDM_MachineWhite;\r
8173     break;\r
8174   case TwoMachinesPlay:\r
8175     nowChecked = IDM_TwoMachines;\r
8176     break;\r
8177   case AnalyzeMode:\r
8178     nowChecked = IDM_AnalysisMode;\r
8179     break;\r
8180   case AnalyzeFile:\r
8181     nowChecked = IDM_AnalyzeFile;\r
8182     break;\r
8183   case EditGame:\r
8184     nowChecked = IDM_EditGame;\r
8185     break;\r
8186   case PlayFromGameFile:\r
8187     nowChecked = IDM_LoadGame;\r
8188     break;\r
8189   case EditPosition:\r
8190     nowChecked = IDM_EditPosition;\r
8191     break;\r
8192   case Training:\r
8193     nowChecked = IDM_Training;\r
8194     break;\r
8195   case IcsPlayingWhite:\r
8196   case IcsPlayingBlack:\r
8197   case IcsObserving:\r
8198   case IcsIdle:\r
8199     nowChecked = IDM_IcsClient;\r
8200     break;\r
8201   default:\r
8202   case EndOfGame:\r
8203     nowChecked = 0;\r
8204     break;\r
8205   }\r
8206   if (prevChecked != 0)\r
8207     (void) CheckMenuItem(GetMenu(hwndMain),\r
8208                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8209   if (nowChecked != 0)\r
8210     (void) CheckMenuItem(GetMenu(hwndMain),\r
8211                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8212 \r
8213   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8214     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8215                           MF_BYCOMMAND|MF_ENABLED);\r
8216   } else {\r
8217     (void) EnableMenuItem(GetMenu(hwndMain), \r
8218                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8219   }\r
8220 \r
8221   prevChecked = nowChecked;\r
8222 }\r
8223 \r
8224 VOID\r
8225 SetICSMode()\r
8226 {\r
8227   HMENU hmenu = GetMenu(hwndMain);\r
8228   SetMenuEnables(hmenu, icsEnables);\r
8229   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8230     MF_BYPOSITION|MF_ENABLED);\r
8231 #ifdef ZIPPY\r
8232   if (appData.zippyPlay) {\r
8233     SetMenuEnables(hmenu, zippyEnables);\r
8234   }\r
8235 #endif\r
8236 }\r
8237 \r
8238 VOID\r
8239 SetGNUMode()\r
8240 {\r
8241   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8242 }\r
8243 \r
8244 VOID\r
8245 SetNCPMode()\r
8246 {\r
8247   HMENU hmenu = GetMenu(hwndMain);\r
8248   SetMenuEnables(hmenu, ncpEnables);\r
8249   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8250     MF_BYPOSITION|MF_GRAYED);\r
8251     DrawMenuBar(hwndMain);\r
8252 }\r
8253 \r
8254 VOID\r
8255 SetCmailMode()\r
8256 {\r
8257   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8258 }\r
8259 \r
8260 VOID \r
8261 SetTrainingModeOn()\r
8262 {\r
8263   int i;\r
8264   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8265   for (i = 0; i < N_BUTTONS; i++) {\r
8266     if (buttonDesc[i].hwnd != NULL)\r
8267       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8268   }\r
8269   CommentPopDown();\r
8270 }\r
8271 \r
8272 VOID SetTrainingModeOff()\r
8273 {\r
8274   int i;\r
8275   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8276   for (i = 0; i < N_BUTTONS; i++) {\r
8277     if (buttonDesc[i].hwnd != NULL)\r
8278       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8279   }\r
8280 }\r
8281 \r
8282 \r
8283 VOID\r
8284 SetUserThinkingEnables()\r
8285 {\r
8286   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8287 }\r
8288 \r
8289 VOID\r
8290 SetMachineThinkingEnables()\r
8291 {\r
8292   HMENU hMenu = GetMenu(hwndMain);\r
8293   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8294 \r
8295   SetMenuEnables(hMenu, machineThinkingEnables);\r
8296 \r
8297   if (gameMode == MachinePlaysBlack) {\r
8298     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8299   } else if (gameMode == MachinePlaysWhite) {\r
8300     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8301   } else if (gameMode == TwoMachinesPlay) {\r
8302     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8303   }\r
8304 }\r
8305 \r
8306 \r
8307 VOID\r
8308 DisplayTitle(char *str)\r
8309 {\r
8310   char title[MSG_SIZ], *host;\r
8311   if (str[0] != NULLCHAR) {\r
8312     strcpy(title, str);\r
8313   } else if (appData.icsActive) {\r
8314     if (appData.icsCommPort[0] != NULLCHAR)\r
8315       host = "ICS";\r
8316     else \r
8317       host = appData.icsHost;\r
8318     sprintf(title, "%s: %s", szTitle, host);\r
8319   } else if (appData.noChessProgram) {\r
8320     strcpy(title, szTitle);\r
8321   } else {\r
8322     strcpy(title, szTitle);\r
8323     strcat(title, ": ");\r
8324     strcat(title, first.tidy);\r
8325   }\r
8326   SetWindowText(hwndMain, title);\r
8327 }\r
8328 \r
8329 \r
8330 VOID\r
8331 DisplayMessage(char *str1, char *str2)\r
8332 {\r
8333   HDC hdc;\r
8334   HFONT oldFont;\r
8335   int remain = MESSAGE_TEXT_MAX - 1;\r
8336   int len;\r
8337 \r
8338   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8339   messageText[0] = NULLCHAR;\r
8340   if (*str1) {\r
8341     len = strlen(str1);\r
8342     if (len > remain) len = remain;\r
8343     strncpy(messageText, str1, len);\r
8344     messageText[len] = NULLCHAR;\r
8345     remain -= len;\r
8346   }\r
8347   if (*str2 && remain >= 2) {\r
8348     if (*str1) {\r
8349       strcat(messageText, "  ");\r
8350       remain -= 2;\r
8351     }\r
8352     len = strlen(str2);\r
8353     if (len > remain) len = remain;\r
8354     strncat(messageText, str2, len);\r
8355   }\r
8356   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8357 \r
8358   if (IsIconic(hwndMain)) return;\r
8359   hdc = GetDC(hwndMain);\r
8360   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8361   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8362              &messageRect, messageText, strlen(messageText), NULL);\r
8363   (void) SelectObject(hdc, oldFont);\r
8364   (void) ReleaseDC(hwndMain, hdc);\r
8365 }\r
8366 \r
8367 VOID\r
8368 DisplayError(char *str, int error)\r
8369 {\r
8370   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8371   int len;\r
8372 \r
8373   if (error == 0) {\r
8374     strcpy(buf, str);\r
8375   } else {\r
8376     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8377                         NULL, error, LANG_NEUTRAL,\r
8378                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8379     if (len > 0) {\r
8380       sprintf(buf, "%s:\n%s", str, buf2);\r
8381     } else {\r
8382       ErrorMap *em = errmap;\r
8383       while (em->err != 0 && em->err != error) em++;\r
8384       if (em->err != 0) {\r
8385         sprintf(buf, "%s:\n%s", str, em->msg);\r
8386       } else {\r
8387         sprintf(buf, "%s:\nError code %d", str, error);\r
8388       }\r
8389     }\r
8390   }\r
8391   \r
8392   ErrorPopUp("Error", buf);\r
8393 }\r
8394 \r
8395 \r
8396 VOID\r
8397 DisplayMoveError(char *str)\r
8398 {\r
8399   fromX = fromY = -1;\r
8400   ClearHighlights();\r
8401   DrawPosition(FALSE, NULL);\r
8402   if (appData.popupMoveErrors) {\r
8403     ErrorPopUp("Error", str);\r
8404   } else {\r
8405     DisplayMessage(str, "");\r
8406     moveErrorMessageUp = TRUE;\r
8407   }\r
8408 }\r
8409 \r
8410 VOID\r
8411 DisplayFatalError(char *str, int error, int exitStatus)\r
8412 {\r
8413   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8414   int len;\r
8415   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8416 \r
8417   if (error != 0) {\r
8418     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8419                         NULL, error, LANG_NEUTRAL,\r
8420                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8421     if (len > 0) {\r
8422       sprintf(buf, "%s:\n%s", str, buf2);\r
8423     } else {\r
8424       ErrorMap *em = errmap;\r
8425       while (em->err != 0 && em->err != error) em++;\r
8426       if (em->err != 0) {\r
8427         sprintf(buf, "%s:\n%s", str, em->msg);\r
8428       } else {\r
8429         sprintf(buf, "%s:\nError code %d", str, error);\r
8430       }\r
8431     }\r
8432     str = buf;\r
8433   }\r
8434   if (appData.debugMode) {\r
8435     fprintf(debugFP, "%s: %s\n", label, str);\r
8436   }\r
8437   if (appData.popupExitMessage) {\r
8438     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8439                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8440   }\r
8441   ExitEvent(exitStatus);\r
8442 }\r
8443 \r
8444 \r
8445 VOID\r
8446 DisplayInformation(char *str)\r
8447 {\r
8448   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8449 }\r
8450 \r
8451 \r
8452 VOID\r
8453 DisplayNote(char *str)\r
8454 {\r
8455   ErrorPopUp("Note", str);\r
8456 }\r
8457 \r
8458 \r
8459 typedef struct {\r
8460   char *title, *question, *replyPrefix;\r
8461   ProcRef pr;\r
8462 } QuestionParams;\r
8463 \r
8464 LRESULT CALLBACK\r
8465 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8466 {\r
8467   static QuestionParams *qp;\r
8468   char reply[MSG_SIZ];\r
8469   int len, err;\r
8470 \r
8471   switch (message) {\r
8472   case WM_INITDIALOG:\r
8473     qp = (QuestionParams *) lParam;\r
8474     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8475     SetWindowText(hDlg, qp->title);\r
8476     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8477     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8478     return FALSE;\r
8479 \r
8480   case WM_COMMAND:\r
8481     switch (LOWORD(wParam)) {\r
8482     case IDOK:\r
8483       strcpy(reply, qp->replyPrefix);\r
8484       if (*reply) strcat(reply, " ");\r
8485       len = strlen(reply);\r
8486       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8487       strcat(reply, "\n");\r
8488       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8489       EndDialog(hDlg, TRUE);\r
8490       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8491       return TRUE;\r
8492     case IDCANCEL:\r
8493       EndDialog(hDlg, FALSE);\r
8494       return TRUE;\r
8495     default:\r
8496       break;\r
8497     }\r
8498     break;\r
8499   }\r
8500   return FALSE;\r
8501 }\r
8502 \r
8503 VOID\r
8504 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8505 {\r
8506     QuestionParams qp;\r
8507     FARPROC lpProc;\r
8508     \r
8509     qp.title = title;\r
8510     qp.question = question;\r
8511     qp.replyPrefix = replyPrefix;\r
8512     qp.pr = pr;\r
8513     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8514     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8515       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8516     FreeProcInstance(lpProc);\r
8517 }\r
8518 \r
8519 /* [AS] Pick FRC position */\r
8520 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8521 {\r
8522     static int * lpIndexFRC;\r
8523     BOOL index_is_ok;\r
8524     char buf[16];\r
8525 \r
8526     switch( message )\r
8527     {\r
8528     case WM_INITDIALOG:\r
8529         lpIndexFRC = (int *) lParam;\r
8530 \r
8531         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8532 \r
8533         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8534         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8535         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8536         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8537 \r
8538         break;\r
8539 \r
8540     case WM_COMMAND:\r
8541         switch( LOWORD(wParam) ) {\r
8542         case IDOK:\r
8543             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8544             EndDialog( hDlg, 0 );\r
8545             return TRUE;\r
8546         case IDCANCEL:\r
8547             EndDialog( hDlg, 1 );   \r
8548             return TRUE;\r
8549         case IDC_NFG_Edit:\r
8550             if( HIWORD(wParam) == EN_CHANGE ) {\r
8551                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8552 \r
8553                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8554             }\r
8555             return TRUE;\r
8556         case IDC_NFG_Random:\r
8557             sprintf( buf, "%d", myrandom() % 960 );\r
8558             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8559             return TRUE;\r
8560         }\r
8561 \r
8562         break;\r
8563     }\r
8564 \r
8565     return FALSE;\r
8566 }\r
8567 \r
8568 int NewGameFRC()\r
8569 {\r
8570     int result;\r
8571     int index = appData.defaultFrcPosition;\r
8572     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8573 \r
8574     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8575 \r
8576     if( result == 0 ) {\r
8577         appData.defaultFrcPosition = index;\r
8578     }\r
8579 \r
8580     return result;\r
8581 }\r
8582 \r
8583 /* [AS] Game list options */\r
8584 typedef struct {\r
8585     char id;\r
8586     char * name;\r
8587 } GLT_Item;\r
8588 \r
8589 static GLT_Item GLT_ItemInfo[] = {\r
8590     { GLT_EVENT,      "Event" },\r
8591     { GLT_SITE,       "Site" },\r
8592     { GLT_DATE,       "Date" },\r
8593     { GLT_ROUND,      "Round" },\r
8594     { GLT_PLAYERS,    "Players" },\r
8595     { GLT_RESULT,     "Result" },\r
8596     { GLT_WHITE_ELO,  "White Rating" },\r
8597     { GLT_BLACK_ELO,  "Black Rating" },\r
8598     { GLT_TIME_CONTROL,"Time Control" },\r
8599     { GLT_VARIANT,    "Variant" },\r
8600     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8601     { 0, 0 }\r
8602 };\r
8603 \r
8604 const char * GLT_FindItem( char id )\r
8605 {\r
8606     const char * result = 0;\r
8607 \r
8608     GLT_Item * list = GLT_ItemInfo;\r
8609 \r
8610     while( list->id != 0 ) {\r
8611         if( list->id == id ) {\r
8612             result = list->name;\r
8613             break;\r
8614         }\r
8615 \r
8616         list++;\r
8617     }\r
8618 \r
8619     return result;\r
8620 }\r
8621 \r
8622 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8623 {\r
8624     const char * name = GLT_FindItem( id );\r
8625 \r
8626     if( name != 0 ) {\r
8627         if( index >= 0 ) {\r
8628             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8629         }\r
8630         else {\r
8631             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8632         }\r
8633     }\r
8634 }\r
8635 \r
8636 void GLT_TagsToList( HWND hDlg, char * tags )\r
8637 {\r
8638     char * pc = tags;\r
8639 \r
8640     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8641 \r
8642     while( *pc ) {\r
8643         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8644         pc++;\r
8645     }\r
8646 \r
8647     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8648 \r
8649     pc = GLT_ALL_TAGS;\r
8650 \r
8651     while( *pc ) {\r
8652         if( strchr( tags, *pc ) == 0 ) {\r
8653             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8654         }\r
8655         pc++;\r
8656     }\r
8657 \r
8658     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8659 }\r
8660 \r
8661 char GLT_ListItemToTag( HWND hDlg, int index )\r
8662 {\r
8663     char result = '\0';\r
8664     char name[128];\r
8665 \r
8666     GLT_Item * list = GLT_ItemInfo;\r
8667 \r
8668     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8669         while( list->id != 0 ) {\r
8670             if( strcmp( list->name, name ) == 0 ) {\r
8671                 result = list->id;\r
8672                 break;\r
8673             }\r
8674 \r
8675             list++;\r
8676         }\r
8677     }\r
8678 \r
8679     return result;\r
8680 }\r
8681 \r
8682 void GLT_MoveSelection( HWND hDlg, int delta )\r
8683 {\r
8684     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8685     int idx2 = idx1 + delta;\r
8686     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8687 \r
8688     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8689         char buf[128];\r
8690 \r
8691         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8692         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8693         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8694         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8695     }\r
8696 }\r
8697 \r
8698 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8699 {\r
8700     static char glt[64];\r
8701     static char * lpUserGLT;\r
8702 \r
8703     switch( message )\r
8704     {\r
8705     case WM_INITDIALOG:\r
8706         lpUserGLT = (char *) lParam;\r
8707         \r
8708         strcpy( glt, lpUserGLT );\r
8709 \r
8710         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8711 \r
8712         /* Initialize list */\r
8713         GLT_TagsToList( hDlg, glt );\r
8714 \r
8715         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8716 \r
8717         break;\r
8718 \r
8719     case WM_COMMAND:\r
8720         switch( LOWORD(wParam) ) {\r
8721         case IDOK:\r
8722             {\r
8723                 char * pc = lpUserGLT;\r
8724                 int idx = 0;\r
8725                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8726                 char id;\r
8727 \r
8728                 do {\r
8729                     id = GLT_ListItemToTag( hDlg, idx );\r
8730 \r
8731                     *pc++ = id;\r
8732                     idx++;\r
8733                 } while( id != '\0' );\r
8734             }\r
8735             EndDialog( hDlg, 0 );\r
8736             return TRUE;\r
8737         case IDCANCEL:\r
8738             EndDialog( hDlg, 1 );\r
8739             return TRUE;\r
8740 \r
8741         case IDC_GLT_Default:\r
8742             strcpy( glt, GLT_DEFAULT_TAGS );\r
8743             GLT_TagsToList( hDlg, glt );\r
8744             return TRUE;\r
8745 \r
8746         case IDC_GLT_Restore:\r
8747             strcpy( glt, lpUserGLT );\r
8748             GLT_TagsToList( hDlg, glt );\r
8749             return TRUE;\r
8750 \r
8751         case IDC_GLT_Up:\r
8752             GLT_MoveSelection( hDlg, -1 );\r
8753             return TRUE;\r
8754 \r
8755         case IDC_GLT_Down:\r
8756             GLT_MoveSelection( hDlg, +1 );\r
8757             return TRUE;\r
8758         }\r
8759 \r
8760         break;\r
8761     }\r
8762 \r
8763     return FALSE;\r
8764 }\r
8765 \r
8766 int GameListOptions()\r
8767 {\r
8768     char glt[64];\r
8769     int result;\r
8770     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8771 \r
8772     strcpy( glt, appData.gameListTags );\r
8773 \r
8774     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8775 \r
8776     if( result == 0 ) {\r
8777         /* [AS] Memory leak here! */\r
8778         appData.gameListTags = strdup( glt ); \r
8779     }\r
8780 \r
8781     return result;\r
8782 }\r
8783 \r
8784 \r
8785 VOID\r
8786 DisplayIcsInteractionTitle(char *str)\r
8787 {\r
8788   char consoleTitle[MSG_SIZ];\r
8789 \r
8790   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8791   SetWindowText(hwndConsole, consoleTitle);\r
8792 }\r
8793 \r
8794 void\r
8795 DrawPosition(int fullRedraw, Board board)\r
8796 {\r
8797   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8798 }\r
8799 \r
8800 \r
8801 VOID\r
8802 ResetFrontEnd()\r
8803 {\r
8804   fromX = fromY = -1;\r
8805   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8806     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8807     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8808     dragInfo.lastpos = dragInfo.pos;\r
8809     dragInfo.start.x = dragInfo.start.y = -1;\r
8810     dragInfo.from = dragInfo.start;\r
8811     ReleaseCapture();\r
8812     DrawPosition(TRUE, NULL);\r
8813   }\r
8814 }\r
8815 \r
8816 \r
8817 VOID\r
8818 CommentPopUp(char *title, char *str)\r
8819 {\r
8820   HWND hwnd = GetActiveWindow();\r
8821   EitherCommentPopUp(0, title, str, FALSE);\r
8822   SetActiveWindow(hwnd);\r
8823 }\r
8824 \r
8825 VOID\r
8826 CommentPopDown(void)\r
8827 {\r
8828   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8829   if (commentDialog) {\r
8830     ShowWindow(commentDialog, SW_HIDE);\r
8831   }\r
8832   commentDialogUp = FALSE;\r
8833 }\r
8834 \r
8835 VOID\r
8836 EditCommentPopUp(int index, char *title, char *str)\r
8837 {\r
8838   EitherCommentPopUp(index, title, str, TRUE);\r
8839 }\r
8840 \r
8841 \r
8842 VOID\r
8843 RingBell()\r
8844 {\r
8845   MyPlaySound(&sounds[(int)SoundMove]);\r
8846 }\r
8847 \r
8848 VOID PlayIcsWinSound()\r
8849 {\r
8850   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8851 }\r
8852 \r
8853 VOID PlayIcsLossSound()\r
8854 {\r
8855   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8856 }\r
8857 \r
8858 VOID PlayIcsDrawSound()\r
8859 {\r
8860   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8861 }\r
8862 \r
8863 VOID PlayIcsUnfinishedSound()\r
8864 {\r
8865   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8866 }\r
8867 \r
8868 VOID\r
8869 PlayAlarmSound()\r
8870 {\r
8871   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8872 }\r
8873 \r
8874 \r
8875 VOID\r
8876 EchoOn()\r
8877 {\r
8878   HWND hInput;\r
8879   consoleEcho = TRUE;\r
8880   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8881   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8882   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8883 }\r
8884 \r
8885 \r
8886 VOID\r
8887 EchoOff()\r
8888 {\r
8889   CHARFORMAT cf;\r
8890   HWND hInput;\r
8891   consoleEcho = FALSE;\r
8892   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8893   /* This works OK: set text and background both to the same color */\r
8894   cf = consoleCF;\r
8895   cf.crTextColor = COLOR_ECHOOFF;\r
8896   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8897   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8898 }\r
8899 \r
8900 /* No Raw()...? */\r
8901 \r
8902 void Colorize(ColorClass cc, int continuation)\r
8903 {\r
8904   currentColorClass = cc;\r
8905   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8906   consoleCF.crTextColor = textAttribs[cc].color;\r
8907   consoleCF.dwEffects = textAttribs[cc].effects;\r
8908   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8909 }\r
8910 \r
8911 char *\r
8912 UserName()\r
8913 {\r
8914   static char buf[MSG_SIZ];\r
8915   DWORD bufsiz = MSG_SIZ;\r
8916 \r
8917   if (!GetUserName(buf, &bufsiz)) {\r
8918     /*DisplayError("Error getting user name", GetLastError());*/\r
8919     strcpy(buf, "User");\r
8920   }\r
8921   return buf;\r
8922 }\r
8923 \r
8924 char *\r
8925 HostName()\r
8926 {\r
8927   static char buf[MSG_SIZ];\r
8928   DWORD bufsiz = MSG_SIZ;\r
8929 \r
8930   if (!GetComputerName(buf, &bufsiz)) {\r
8931     /*DisplayError("Error getting host name", GetLastError());*/\r
8932     strcpy(buf, "Unknown");\r
8933   }\r
8934   return buf;\r
8935 }\r
8936 \r
8937 \r
8938 int\r
8939 ClockTimerRunning()\r
8940 {\r
8941   return clockTimerEvent != 0;\r
8942 }\r
8943 \r
8944 int\r
8945 StopClockTimer()\r
8946 {\r
8947   if (clockTimerEvent == 0) return FALSE;\r
8948   KillTimer(hwndMain, clockTimerEvent);\r
8949   clockTimerEvent = 0;\r
8950   return TRUE;\r
8951 }\r
8952 \r
8953 void\r
8954 StartClockTimer(long millisec)\r
8955 {\r
8956   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8957                              (UINT) millisec, NULL);\r
8958 }\r
8959 \r
8960 void\r
8961 DisplayWhiteClock(long timeRemaining, int highlight)\r
8962 {\r
8963   HDC hdc;\r
8964   hdc = GetDC(hwndMain);\r
8965   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8966 \r
8967   if (!IsIconic(hwndMain)) {\r
8968     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
8969   }\r
8970   if (highlight && iconCurrent == iconBlack) {\r
8971     iconCurrent = iconWhite;\r
8972     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8973     if (IsIconic(hwndMain)) {\r
8974       DrawIcon(hdc, 2, 2, iconCurrent);\r
8975     }\r
8976   }\r
8977   (void) ReleaseDC(hwndMain, hdc);\r
8978   if (hwndConsole)\r
8979     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8980 }\r
8981 \r
8982 void\r
8983 DisplayBlackClock(long timeRemaining, int highlight)\r
8984 {\r
8985   HDC hdc;\r
8986   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8987 \r
8988   hdc = GetDC(hwndMain);\r
8989   if (!IsIconic(hwndMain)) {\r
8990     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8991   }\r
8992   if (highlight && iconCurrent == iconWhite) {\r
8993     iconCurrent = iconBlack;\r
8994     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8995     if (IsIconic(hwndMain)) {\r
8996       DrawIcon(hdc, 2, 2, iconCurrent);\r
8997     }\r
8998   }\r
8999   (void) ReleaseDC(hwndMain, hdc);\r
9000   if (hwndConsole)\r
9001     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9002 }\r
9003 \r
9004 \r
9005 int\r
9006 LoadGameTimerRunning()\r
9007 {\r
9008   return loadGameTimerEvent != 0;\r
9009 }\r
9010 \r
9011 int\r
9012 StopLoadGameTimer()\r
9013 {\r
9014   if (loadGameTimerEvent == 0) return FALSE;\r
9015   KillTimer(hwndMain, loadGameTimerEvent);\r
9016   loadGameTimerEvent = 0;\r
9017   return TRUE;\r
9018 }\r
9019 \r
9020 void\r
9021 StartLoadGameTimer(long millisec)\r
9022 {\r
9023   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9024                                 (UINT) millisec, NULL);\r
9025 }\r
9026 \r
9027 void\r
9028 AutoSaveGame()\r
9029 {\r
9030   char *defName;\r
9031   FILE *f;\r
9032   char fileTitle[MSG_SIZ];\r
9033 \r
9034   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9035   f = OpenFileDialog(hwndMain, TRUE, defName,\r
9036                      appData.oldSaveStyle ? "gam" : "pgn",\r
9037                      GAME_FILT, \r
9038                      "Save Game to File", NULL, fileTitle, NULL);\r
9039   if (f != NULL) {\r
9040     SaveGame(f, 0, "");\r
9041     fclose(f);\r
9042   }\r
9043 }\r
9044 \r
9045 \r
9046 void\r
9047 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9048 {\r
9049   if (delayedTimerEvent != 0) {\r
9050     if (appData.debugMode) {\r
9051       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9052     }\r
9053     KillTimer(hwndMain, delayedTimerEvent);\r
9054     delayedTimerEvent = 0;\r
9055     delayedTimerCallback();\r
9056   }\r
9057   delayedTimerCallback = cb;\r
9058   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9059                                 (UINT) millisec, NULL);\r
9060 }\r
9061 \r
9062 DelayedEventCallback\r
9063 GetDelayedEvent()\r
9064 {\r
9065   if (delayedTimerEvent) {\r
9066     return delayedTimerCallback;\r
9067   } else {\r
9068     return NULL;\r
9069   }\r
9070 }\r
9071 \r
9072 void\r
9073 CancelDelayedEvent()\r
9074 {\r
9075   if (delayedTimerEvent) {\r
9076     KillTimer(hwndMain, delayedTimerEvent);\r
9077     delayedTimerEvent = 0;\r
9078   }\r
9079 }\r
9080 \r
9081 /* Start a child process running the given program.\r
9082    The process's standard output can be read from "from", and its\r
9083    standard input can be written to "to".\r
9084    Exit with fatal error if anything goes wrong.\r
9085    Returns an opaque pointer that can be used to destroy the process\r
9086    later.\r
9087 */\r
9088 int\r
9089 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9090 {\r
9091 #define BUFSIZE 4096\r
9092 \r
9093   HANDLE hChildStdinRd, hChildStdinWr,\r
9094     hChildStdoutRd, hChildStdoutWr;\r
9095   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9096   SECURITY_ATTRIBUTES saAttr;\r
9097   BOOL fSuccess;\r
9098   PROCESS_INFORMATION piProcInfo;\r
9099   STARTUPINFO siStartInfo;\r
9100   ChildProc *cp;\r
9101   char buf[MSG_SIZ];\r
9102   DWORD err;\r
9103 \r
9104   if (appData.debugMode) {\r
9105     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9106   }\r
9107 \r
9108   *pr = NoProc;\r
9109 \r
9110   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9111   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9112   saAttr.bInheritHandle = TRUE;\r
9113   saAttr.lpSecurityDescriptor = NULL;\r
9114 \r
9115   /*\r
9116    * The steps for redirecting child's STDOUT:\r
9117    *     1. Create anonymous pipe to be STDOUT for child.\r
9118    *     2. Create a noninheritable duplicate of read handle,\r
9119    *         and close the inheritable read handle.\r
9120    */\r
9121 \r
9122   /* Create a pipe for the child's STDOUT. */\r
9123   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9124     return GetLastError();\r
9125   }\r
9126 \r
9127   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9128   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9129                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9130                              FALSE,     /* not inherited */\r
9131                              DUPLICATE_SAME_ACCESS);\r
9132   if (! fSuccess) {\r
9133     return GetLastError();\r
9134   }\r
9135   CloseHandle(hChildStdoutRd);\r
9136 \r
9137   /*\r
9138    * The steps for redirecting child's STDIN:\r
9139    *     1. Create anonymous pipe to be STDIN for child.\r
9140    *     2. Create a noninheritable duplicate of write handle,\r
9141    *         and close the inheritable write handle.\r
9142    */\r
9143 \r
9144   /* Create a pipe for the child's STDIN. */\r
9145   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9146     return GetLastError();\r
9147   }\r
9148 \r
9149   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9150   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9151                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9152                              FALSE,     /* not inherited */\r
9153                              DUPLICATE_SAME_ACCESS);\r
9154   if (! fSuccess) {\r
9155     return GetLastError();\r
9156   }\r
9157   CloseHandle(hChildStdinWr);\r
9158 \r
9159   /* Arrange to (1) look in dir for the child .exe file, and\r
9160    * (2) have dir be the child's working directory.  Interpret\r
9161    * dir relative to the directory WinBoard loaded from. */\r
9162   GetCurrentDirectory(MSG_SIZ, buf);\r
9163   SetCurrentDirectory(installDir);\r
9164   SetCurrentDirectory(dir);\r
9165 \r
9166   /* Now create the child process. */\r
9167 \r
9168   siStartInfo.cb = sizeof(STARTUPINFO);\r
9169   siStartInfo.lpReserved = NULL;\r
9170   siStartInfo.lpDesktop = NULL;\r
9171   siStartInfo.lpTitle = NULL;\r
9172   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9173   siStartInfo.cbReserved2 = 0;\r
9174   siStartInfo.lpReserved2 = NULL;\r
9175   siStartInfo.hStdInput = hChildStdinRd;\r
9176   siStartInfo.hStdOutput = hChildStdoutWr;\r
9177   siStartInfo.hStdError = hChildStdoutWr;\r
9178 \r
9179   fSuccess = CreateProcess(NULL,\r
9180                            cmdLine,        /* command line */\r
9181                            NULL,           /* process security attributes */\r
9182                            NULL,           /* primary thread security attrs */\r
9183                            TRUE,           /* handles are inherited */\r
9184                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9185                            NULL,           /* use parent's environment */\r
9186                            NULL,\r
9187                            &siStartInfo, /* STARTUPINFO pointer */\r
9188                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9189 \r
9190   err = GetLastError();\r
9191   SetCurrentDirectory(buf); /* return to prev directory */\r
9192   if (! fSuccess) {\r
9193     return err;\r
9194   }\r
9195 \r
9196   /* Close the handles we don't need in the parent */\r
9197   CloseHandle(piProcInfo.hThread);\r
9198   CloseHandle(hChildStdinRd);\r
9199   CloseHandle(hChildStdoutWr);\r
9200 \r
9201   /* Prepare return value */\r
9202   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9203   cp->kind = CPReal;\r
9204   cp->hProcess = piProcInfo.hProcess;\r
9205   cp->pid = piProcInfo.dwProcessId;\r
9206   cp->hFrom = hChildStdoutRdDup;\r
9207   cp->hTo = hChildStdinWrDup;\r
9208 \r
9209   *pr = (void *) cp;\r
9210 \r
9211   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9212      2000 where engines sometimes don't see the initial command(s)\r
9213      from WinBoard and hang.  I don't understand how that can happen,\r
9214      but the Sleep is harmless, so I've put it in.  Others have also\r
9215      reported what may be the same problem, so hopefully this will fix\r
9216      it for them too.  */\r
9217   Sleep(500);\r
9218 \r
9219   return NO_ERROR;\r
9220 }\r
9221 \r
9222 \r
9223 void\r
9224 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9225 {\r
9226   ChildProc *cp; int result;\r
9227 \r
9228   cp = (ChildProc *) pr;\r
9229   if (cp == NULL) return;\r
9230 \r
9231   switch (cp->kind) {\r
9232   case CPReal:\r
9233     /* TerminateProcess is considered harmful, so... */\r
9234     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9235     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9236     /* The following doesn't work because the chess program\r
9237        doesn't "have the same console" as WinBoard.  Maybe\r
9238        we could arrange for this even though neither WinBoard\r
9239        nor the chess program uses a console for stdio? */\r
9240     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9241 \r
9242     /* [AS] Special termination modes for misbehaving programs... */\r
9243     if( signal == 9 ) { \r
9244         result = TerminateProcess( cp->hProcess, 0 );\r
9245 \r
9246         if ( appData.debugMode) {\r
9247             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9248         }\r
9249     }\r
9250     else if( signal == 10 ) {\r
9251         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9252 \r
9253         if( dw != WAIT_OBJECT_0 ) {\r
9254             result = TerminateProcess( cp->hProcess, 0 );\r
9255 \r
9256             if ( appData.debugMode) {\r
9257                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9258             }\r
9259 \r
9260         }\r
9261     }\r
9262 \r
9263     CloseHandle(cp->hProcess);\r
9264     break;\r
9265 \r
9266   case CPComm:\r
9267     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9268     break;\r
9269 \r
9270   case CPSock:\r
9271     closesocket(cp->sock);\r
9272     WSACleanup();\r
9273     break;\r
9274 \r
9275   case CPRcmd:\r
9276     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9277     closesocket(cp->sock);\r
9278     closesocket(cp->sock2);\r
9279     WSACleanup();\r
9280     break;\r
9281   }\r
9282   free(cp);\r
9283 }\r
9284 \r
9285 void\r
9286 InterruptChildProcess(ProcRef pr)\r
9287 {\r
9288   ChildProc *cp;\r
9289 \r
9290   cp = (ChildProc *) pr;\r
9291   if (cp == NULL) return;\r
9292   switch (cp->kind) {\r
9293   case CPReal:\r
9294     /* The following doesn't work because the chess program\r
9295        doesn't "have the same console" as WinBoard.  Maybe\r
9296        we could arrange for this even though neither WinBoard\r
9297        nor the chess program uses a console for stdio */\r
9298     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9299     break;\r
9300 \r
9301   case CPComm:\r
9302   case CPSock:\r
9303     /* Can't interrupt */\r
9304     break;\r
9305 \r
9306   case CPRcmd:\r
9307     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9308     break;\r
9309   }\r
9310 }\r
9311 \r
9312 \r
9313 int\r
9314 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9315 {\r
9316   char cmdLine[MSG_SIZ];\r
9317 \r
9318   if (port[0] == NULLCHAR) {\r
9319     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9320   } else {\r
9321     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9322   }\r
9323   return StartChildProcess(cmdLine, "", pr);\r
9324 }\r
9325 \r
9326 \r
9327 /* Code to open TCP sockets */\r
9328 \r
9329 int\r
9330 OpenTCP(char *host, char *port, ProcRef *pr)\r
9331 {\r
9332   ChildProc *cp;\r
9333   int err;\r
9334   SOCKET s;\r
9335   struct sockaddr_in sa, mysa;\r
9336   struct hostent FAR *hp;\r
9337   unsigned short uport;\r
9338   WORD wVersionRequested;\r
9339   WSADATA wsaData;\r
9340 \r
9341   /* Initialize socket DLL */\r
9342   wVersionRequested = MAKEWORD(1, 1);\r
9343   err = WSAStartup(wVersionRequested, &wsaData);\r
9344   if (err != 0) return err;\r
9345 \r
9346   /* Make socket */\r
9347   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9348     err = WSAGetLastError();\r
9349     WSACleanup();\r
9350     return err;\r
9351   }\r
9352 \r
9353   /* Bind local address using (mostly) don't-care values.\r
9354    */\r
9355   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9356   mysa.sin_family = AF_INET;\r
9357   mysa.sin_addr.s_addr = INADDR_ANY;\r
9358   uport = (unsigned short) 0;\r
9359   mysa.sin_port = htons(uport);\r
9360   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9361       == SOCKET_ERROR) {\r
9362     err = WSAGetLastError();\r
9363     WSACleanup();\r
9364     return err;\r
9365   }\r
9366 \r
9367   /* Resolve remote host name */\r
9368   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9369   if (!(hp = gethostbyname(host))) {\r
9370     unsigned int b0, b1, b2, b3;\r
9371 \r
9372     err = WSAGetLastError();\r
9373 \r
9374     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9375       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9376       hp->h_addrtype = AF_INET;\r
9377       hp->h_length = 4;\r
9378       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9379       hp->h_addr_list[0] = (char *) malloc(4);\r
9380       hp->h_addr_list[0][0] = (char) b0;\r
9381       hp->h_addr_list[0][1] = (char) b1;\r
9382       hp->h_addr_list[0][2] = (char) b2;\r
9383       hp->h_addr_list[0][3] = (char) b3;\r
9384     } else {\r
9385       WSACleanup();\r
9386       return err;\r
9387     }\r
9388   }\r
9389   sa.sin_family = hp->h_addrtype;\r
9390   uport = (unsigned short) atoi(port);\r
9391   sa.sin_port = htons(uport);\r
9392   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9393 \r
9394   /* Make connection */\r
9395   if (connect(s, (struct sockaddr *) &sa,\r
9396               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9397     err = WSAGetLastError();\r
9398     WSACleanup();\r
9399     return err;\r
9400   }\r
9401 \r
9402   /* Prepare return value */\r
9403   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9404   cp->kind = CPSock;\r
9405   cp->sock = s;\r
9406   *pr = (ProcRef *) cp;\r
9407 \r
9408   return NO_ERROR;\r
9409 }\r
9410 \r
9411 int\r
9412 OpenCommPort(char *name, ProcRef *pr)\r
9413 {\r
9414   HANDLE h;\r
9415   COMMTIMEOUTS ct;\r
9416   ChildProc *cp;\r
9417   char fullname[MSG_SIZ];\r
9418 \r
9419   if (*name != '\\')\r
9420     sprintf(fullname, "\\\\.\\%s", name);\r
9421   else\r
9422     strcpy(fullname, name);\r
9423 \r
9424   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9425                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9426   if (h == (HANDLE) -1) {\r
9427     return GetLastError();\r
9428   }\r
9429   hCommPort = h;\r
9430 \r
9431   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9432 \r
9433   /* Accumulate characters until a 100ms pause, then parse */\r
9434   ct.ReadIntervalTimeout = 100;\r
9435   ct.ReadTotalTimeoutMultiplier = 0;\r
9436   ct.ReadTotalTimeoutConstant = 0;\r
9437   ct.WriteTotalTimeoutMultiplier = 0;\r
9438   ct.WriteTotalTimeoutConstant = 0;\r
9439   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9440 \r
9441   /* Prepare return value */\r
9442   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9443   cp->kind = CPComm;\r
9444   cp->hFrom = h;\r
9445   cp->hTo = h;\r
9446   *pr = (ProcRef *) cp;\r
9447 \r
9448   return NO_ERROR;\r
9449 }\r
9450 \r
9451 int\r
9452 OpenLoopback(ProcRef *pr)\r
9453 {\r
9454   DisplayFatalError("Not implemented", 0, 1);\r
9455   return NO_ERROR;\r
9456 }\r
9457 \r
9458 \r
9459 int\r
9460 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9461 {\r
9462   ChildProc *cp;\r
9463   int err;\r
9464   SOCKET s, s2, s3;\r
9465   struct sockaddr_in sa, mysa;\r
9466   struct hostent FAR *hp;\r
9467   unsigned short uport;\r
9468   WORD wVersionRequested;\r
9469   WSADATA wsaData;\r
9470   int fromPort;\r
9471   char stderrPortStr[MSG_SIZ];\r
9472 \r
9473   /* Initialize socket DLL */\r
9474   wVersionRequested = MAKEWORD(1, 1);\r
9475   err = WSAStartup(wVersionRequested, &wsaData);\r
9476   if (err != 0) return err;\r
9477 \r
9478   /* Resolve remote host name */\r
9479   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9480   if (!(hp = gethostbyname(host))) {\r
9481     unsigned int b0, b1, b2, b3;\r
9482 \r
9483     err = WSAGetLastError();\r
9484 \r
9485     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9486       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9487       hp->h_addrtype = AF_INET;\r
9488       hp->h_length = 4;\r
9489       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9490       hp->h_addr_list[0] = (char *) malloc(4);\r
9491       hp->h_addr_list[0][0] = (char) b0;\r
9492       hp->h_addr_list[0][1] = (char) b1;\r
9493       hp->h_addr_list[0][2] = (char) b2;\r
9494       hp->h_addr_list[0][3] = (char) b3;\r
9495     } else {\r
9496       WSACleanup();\r
9497       return err;\r
9498     }\r
9499   }\r
9500   sa.sin_family = hp->h_addrtype;\r
9501   uport = (unsigned short) 514;\r
9502   sa.sin_port = htons(uport);\r
9503   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9504 \r
9505   /* Bind local socket to unused "privileged" port address\r
9506    */\r
9507   s = INVALID_SOCKET;\r
9508   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9509   mysa.sin_family = AF_INET;\r
9510   mysa.sin_addr.s_addr = INADDR_ANY;\r
9511   for (fromPort = 1023;; fromPort--) {\r
9512     if (fromPort < 0) {\r
9513       WSACleanup();\r
9514       return WSAEADDRINUSE;\r
9515     }\r
9516     if (s == INVALID_SOCKET) {\r
9517       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9518         err = WSAGetLastError();\r
9519         WSACleanup();\r
9520         return err;\r
9521       }\r
9522     }\r
9523     uport = (unsigned short) fromPort;\r
9524     mysa.sin_port = htons(uport);\r
9525     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9526         == SOCKET_ERROR) {\r
9527       err = WSAGetLastError();\r
9528       if (err == WSAEADDRINUSE) continue;\r
9529       WSACleanup();\r
9530       return err;\r
9531     }\r
9532     if (connect(s, (struct sockaddr *) &sa,\r
9533       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9534       err = WSAGetLastError();\r
9535       if (err == WSAEADDRINUSE) {\r
9536         closesocket(s);\r
9537         s = -1;\r
9538         continue;\r
9539       }\r
9540       WSACleanup();\r
9541       return err;\r
9542     }\r
9543     break;\r
9544   }\r
9545 \r
9546   /* Bind stderr local socket to unused "privileged" port address\r
9547    */\r
9548   s2 = INVALID_SOCKET;\r
9549   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9550   mysa.sin_family = AF_INET;\r
9551   mysa.sin_addr.s_addr = INADDR_ANY;\r
9552   for (fromPort = 1023;; fromPort--) {\r
9553     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9554     if (fromPort < 0) {\r
9555       (void) closesocket(s);\r
9556       WSACleanup();\r
9557       return WSAEADDRINUSE;\r
9558     }\r
9559     if (s2 == INVALID_SOCKET) {\r
9560       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9561         err = WSAGetLastError();\r
9562         closesocket(s);\r
9563         WSACleanup();\r
9564         return err;\r
9565       }\r
9566     }\r
9567     uport = (unsigned short) fromPort;\r
9568     mysa.sin_port = htons(uport);\r
9569     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9570         == SOCKET_ERROR) {\r
9571       err = WSAGetLastError();\r
9572       if (err == WSAEADDRINUSE) continue;\r
9573       (void) closesocket(s);\r
9574       WSACleanup();\r
9575       return err;\r
9576     }\r
9577     if (listen(s2, 1) == SOCKET_ERROR) {\r
9578       err = WSAGetLastError();\r
9579       if (err == WSAEADDRINUSE) {\r
9580         closesocket(s2);\r
9581         s2 = INVALID_SOCKET;\r
9582         continue;\r
9583       }\r
9584       (void) closesocket(s);\r
9585       (void) closesocket(s2);\r
9586       WSACleanup();\r
9587       return err;\r
9588     }\r
9589     break;\r
9590   }\r
9591   prevStderrPort = fromPort; // remember port used\r
9592   sprintf(stderrPortStr, "%d", fromPort);\r
9593 \r
9594   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9595     err = WSAGetLastError();\r
9596     (void) closesocket(s);\r
9597     (void) closesocket(s2);\r
9598     WSACleanup();\r
9599     return err;\r
9600   }\r
9601 \r
9602   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9603     err = WSAGetLastError();\r
9604     (void) closesocket(s);\r
9605     (void) closesocket(s2);\r
9606     WSACleanup();\r
9607     return err;\r
9608   }\r
9609   if (*user == NULLCHAR) user = UserName();\r
9610   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9611     err = WSAGetLastError();\r
9612     (void) closesocket(s);\r
9613     (void) closesocket(s2);\r
9614     WSACleanup();\r
9615     return err;\r
9616   }\r
9617   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9618     err = WSAGetLastError();\r
9619     (void) closesocket(s);\r
9620     (void) closesocket(s2);\r
9621     WSACleanup();\r
9622     return err;\r
9623   }\r
9624 \r
9625   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9626     err = WSAGetLastError();\r
9627     (void) closesocket(s);\r
9628     (void) closesocket(s2);\r
9629     WSACleanup();\r
9630     return err;\r
9631   }\r
9632   (void) closesocket(s2);  /* Stop listening */\r
9633 \r
9634   /* Prepare return value */\r
9635   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9636   cp->kind = CPRcmd;\r
9637   cp->sock = s;\r
9638   cp->sock2 = s3;\r
9639   *pr = (ProcRef *) cp;\r
9640 \r
9641   return NO_ERROR;\r
9642 }\r
9643 \r
9644 \r
9645 InputSourceRef\r
9646 AddInputSource(ProcRef pr, int lineByLine,\r
9647                InputCallback func, VOIDSTAR closure)\r
9648 {\r
9649   InputSource *is, *is2 = NULL;\r
9650   ChildProc *cp = (ChildProc *) pr;\r
9651 \r
9652   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9653   is->lineByLine = lineByLine;\r
9654   is->func = func;\r
9655   is->closure = closure;\r
9656   is->second = NULL;\r
9657   is->next = is->buf;\r
9658   if (pr == NoProc) {\r
9659     is->kind = CPReal;\r
9660     consoleInputSource = is;\r
9661   } else {\r
9662     is->kind = cp->kind;\r
9663     /* \r
9664         [AS] Try to avoid a race condition if the thread is given control too early:\r
9665         we create all threads suspended so that the is->hThread variable can be\r
9666         safely assigned, then let the threads start with ResumeThread.\r
9667     */\r
9668     switch (cp->kind) {\r
9669     case CPReal:\r
9670       is->hFile = cp->hFrom;\r
9671       cp->hFrom = NULL; /* now owned by InputThread */\r
9672       is->hThread =\r
9673         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9674                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9675       break;\r
9676 \r
9677     case CPComm:\r
9678       is->hFile = cp->hFrom;\r
9679       cp->hFrom = NULL; /* now owned by InputThread */\r
9680       is->hThread =\r
9681         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9682                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9683       break;\r
9684 \r
9685     case CPSock:\r
9686       is->sock = cp->sock;\r
9687       is->hThread =\r
9688         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9689                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9690       break;\r
9691 \r
9692     case CPRcmd:\r
9693       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9694       *is2 = *is;\r
9695       is->sock = cp->sock;\r
9696       is->second = is2;\r
9697       is2->sock = cp->sock2;\r
9698       is2->second = is2;\r
9699       is->hThread =\r
9700         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9701                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9702       is2->hThread =\r
9703         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9704                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9705       break;\r
9706     }\r
9707 \r
9708     if( is->hThread != NULL ) {\r
9709         ResumeThread( is->hThread );\r
9710     }\r
9711 \r
9712     if( is2 != NULL && is2->hThread != NULL ) {\r
9713         ResumeThread( is2->hThread );\r
9714     }\r
9715   }\r
9716 \r
9717   return (InputSourceRef) is;\r
9718 }\r
9719 \r
9720 void\r
9721 RemoveInputSource(InputSourceRef isr)\r
9722 {\r
9723   InputSource *is;\r
9724 \r
9725   is = (InputSource *) isr;\r
9726   is->hThread = NULL;  /* tell thread to stop */\r
9727   CloseHandle(is->hThread);\r
9728   if (is->second != NULL) {\r
9729     is->second->hThread = NULL;\r
9730     CloseHandle(is->second->hThread);\r
9731   }\r
9732 }\r
9733 \r
9734 \r
9735 int\r
9736 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9737 {\r
9738   DWORD dOutCount;\r
9739   int outCount = SOCKET_ERROR;\r
9740   ChildProc *cp = (ChildProc *) pr;\r
9741   static OVERLAPPED ovl;\r
9742 \r
9743   if (pr == NoProc) {\r
9744     ConsoleOutput(message, count, FALSE);\r
9745     return count;\r
9746   } \r
9747 \r
9748   if (ovl.hEvent == NULL) {\r
9749     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9750   }\r
9751   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9752 \r
9753   switch (cp->kind) {\r
9754   case CPSock:\r
9755   case CPRcmd:\r
9756     outCount = send(cp->sock, message, count, 0);\r
9757     if (outCount == SOCKET_ERROR) {\r
9758       *outError = WSAGetLastError();\r
9759     } else {\r
9760       *outError = NO_ERROR;\r
9761     }\r
9762     break;\r
9763 \r
9764   case CPReal:\r
9765     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9766                   &dOutCount, NULL)) {\r
9767       *outError = NO_ERROR;\r
9768       outCount = (int) dOutCount;\r
9769     } else {\r
9770       *outError = GetLastError();\r
9771     }\r
9772     break;\r
9773 \r
9774   case CPComm:\r
9775     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9776                             &dOutCount, &ovl);\r
9777     if (*outError == NO_ERROR) {\r
9778       outCount = (int) dOutCount;\r
9779     }\r
9780     break;\r
9781   }\r
9782   return outCount;\r
9783 }\r
9784 \r
9785 int\r
9786 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9787                        long msdelay)\r
9788 {\r
9789   /* Ignore delay, not implemented for WinBoard */\r
9790   return OutputToProcess(pr, message, count, outError);\r
9791 }\r
9792 \r
9793 \r
9794 void\r
9795 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9796                         char *buf, int count, int error)\r
9797 {\r
9798   DisplayFatalError("Not implemented", 0, 1);\r
9799 }\r
9800 \r
9801 /* see wgamelist.c for Game List functions */\r
9802 /* see wedittags.c for Edit Tags functions */\r
9803 \r
9804 \r
9805 VOID\r
9806 ICSInitScript()\r
9807 {\r
9808   FILE *f;\r
9809   char buf[MSG_SIZ];\r
9810   char *dummy;\r
9811 \r
9812   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9813     f = fopen(buf, "r");\r
9814     if (f != NULL) {\r
9815       ProcessICSInitScript(f);\r
9816       fclose(f);\r
9817     }\r
9818   }\r
9819 }\r
9820 \r
9821 \r
9822 VOID\r
9823 StartAnalysisClock()\r
9824 {\r
9825   if (analysisTimerEvent) return;\r
9826   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9827                                         (UINT) 2000, NULL);\r
9828 }\r
9829 \r
9830 LRESULT CALLBACK\r
9831 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9832 {\r
9833   static HANDLE hwndText;\r
9834   RECT rect;\r
9835   static int sizeX, sizeY;\r
9836   int newSizeX, newSizeY, flags;\r
9837   MINMAXINFO *mmi;\r
9838 \r
9839   switch (message) {\r
9840   case WM_INITDIALOG: /* message: initialize dialog box */\r
9841     /* Initialize the dialog items */\r
9842     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9843     SetWindowText(hDlg, analysisTitle);\r
9844     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9845     /* Size and position the dialog */\r
9846     if (!analysisDialog) {\r
9847       analysisDialog = hDlg;\r
9848       flags = SWP_NOZORDER;\r
9849       GetClientRect(hDlg, &rect);\r
9850       sizeX = rect.right;\r
9851       sizeY = rect.bottom;\r
9852       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9853           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9854         WINDOWPLACEMENT wp;\r
9855         EnsureOnScreen(&analysisX, &analysisY);\r
9856         wp.length = sizeof(WINDOWPLACEMENT);\r
9857         wp.flags = 0;\r
9858         wp.showCmd = SW_SHOW;\r
9859         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9860         wp.rcNormalPosition.left = analysisX;\r
9861         wp.rcNormalPosition.right = analysisX + analysisW;\r
9862         wp.rcNormalPosition.top = analysisY;\r
9863         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9864         SetWindowPlacement(hDlg, &wp);\r
9865 \r
9866         GetClientRect(hDlg, &rect);\r
9867         newSizeX = rect.right;\r
9868         newSizeY = rect.bottom;\r
9869         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9870                               newSizeX, newSizeY);\r
9871         sizeX = newSizeX;\r
9872         sizeY = newSizeY;\r
9873       }\r
9874     }\r
9875     return FALSE;\r
9876 \r
9877   case WM_COMMAND: /* message: received a command */\r
9878     switch (LOWORD(wParam)) {\r
9879     case IDCANCEL:\r
9880       EditGameEvent();\r
9881       return TRUE;\r
9882     default:\r
9883       break;\r
9884     }\r
9885     break;\r
9886 \r
9887   case WM_SIZE:\r
9888     newSizeX = LOWORD(lParam);\r
9889     newSizeY = HIWORD(lParam);\r
9890     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9891     sizeX = newSizeX;\r
9892     sizeY = newSizeY;\r
9893     break;\r
9894 \r
9895   case WM_GETMINMAXINFO:\r
9896     /* Prevent resizing window too small */\r
9897     mmi = (MINMAXINFO *) lParam;\r
9898     mmi->ptMinTrackSize.x = 100;\r
9899     mmi->ptMinTrackSize.y = 100;\r
9900     break;\r
9901   }\r
9902   return FALSE;\r
9903 }\r
9904 \r
9905 VOID\r
9906 AnalysisPopUp(char* title, char* str)\r
9907 {\r
9908   FARPROC lpProc;\r
9909   char *p, *q;\r
9910 \r
9911   /* [AS] */\r
9912   EngineOutputPopUp();\r
9913   return;\r
9914 \r
9915   if (str == NULL) str = "";\r
9916   p = (char *) malloc(2 * strlen(str) + 2);\r
9917   q = p;\r
9918   while (*str) {\r
9919     if (*str == '\n') *q++ = '\r';\r
9920     *q++ = *str++;\r
9921   }\r
9922   *q = NULLCHAR;\r
9923   if (analysisText != NULL) free(analysisText);\r
9924   analysisText = p;\r
9925 \r
9926   if (analysisDialog) {\r
9927     SetWindowText(analysisDialog, title);\r
9928     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9929     ShowWindow(analysisDialog, SW_SHOW);\r
9930   } else {\r
9931     analysisTitle = title;\r
9932     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9933     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9934                  hwndMain, (DLGPROC)lpProc);\r
9935     FreeProcInstance(lpProc);\r
9936   }\r
9937   analysisDialogUp = TRUE;  \r
9938 }\r
9939 \r
9940 VOID\r
9941 AnalysisPopDown()\r
9942 {\r
9943   if (analysisDialog) {\r
9944     ShowWindow(analysisDialog, SW_HIDE);\r
9945   }\r
9946   analysisDialogUp = FALSE;  \r
9947 }\r
9948 \r
9949 \r
9950 VOID\r
9951 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9952 {\r
9953   highlightInfo.sq[0].x = fromX;\r
9954   highlightInfo.sq[0].y = fromY;\r
9955   highlightInfo.sq[1].x = toX;\r
9956   highlightInfo.sq[1].y = toY;\r
9957 }\r
9958 \r
9959 VOID\r
9960 ClearHighlights()\r
9961 {\r
9962   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9963     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9964 }\r
9965 \r
9966 VOID\r
9967 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9968 {\r
9969   premoveHighlightInfo.sq[0].x = fromX;\r
9970   premoveHighlightInfo.sq[0].y = fromY;\r
9971   premoveHighlightInfo.sq[1].x = toX;\r
9972   premoveHighlightInfo.sq[1].y = toY;\r
9973 }\r
9974 \r
9975 VOID\r
9976 ClearPremoveHighlights()\r
9977 {\r
9978   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9979     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9980 }\r
9981 \r
9982 VOID\r
9983 ShutDownFrontEnd()\r
9984 {\r
9985   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9986   DeleteClipboardTempFiles();\r
9987 }\r
9988 \r
9989 void\r
9990 BoardToTop()\r
9991 {\r
9992     if (IsIconic(hwndMain))\r
9993       ShowWindow(hwndMain, SW_RESTORE);\r
9994 \r
9995     SetActiveWindow(hwndMain);\r
9996 }\r
9997 \r
9998 /*\r
9999  * Prototypes for animation support routines\r
10000  */\r
10001 static void ScreenSquare(int column, int row, POINT * pt);\r
10002 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10003      POINT frames[], int * nFrames);\r
10004 \r
10005 \r
10006 #define kFactor 4\r
10007 \r
10008 void\r
10009 AnimateMove(board, fromX, fromY, toX, toY)\r
10010      Board board;\r
10011      int fromX;\r
10012      int fromY;\r
10013      int toX;\r
10014      int toY;\r
10015 {\r
10016   ChessSquare piece;\r
10017   POINT start, finish, mid;\r
10018   POINT frames[kFactor * 2 + 1];\r
10019   int nFrames, n;\r
10020 \r
10021   if (!appData.animate) return;\r
10022   if (doingSizing) return;\r
10023   if (fromY < 0 || fromX < 0) return;\r
10024   piece = board[fromY][fromX];\r
10025   if (piece >= EmptySquare) return;\r
10026 \r
10027   ScreenSquare(fromX, fromY, &start);\r
10028   ScreenSquare(toX, toY, &finish);\r
10029 \r
10030   /* All pieces except knights move in straight line */\r
10031   if (piece != WhiteKnight && piece != BlackKnight) {\r
10032     mid.x = start.x + (finish.x - start.x) / 2;\r
10033     mid.y = start.y + (finish.y - start.y) / 2;\r
10034   } else {\r
10035     /* Knight: make diagonal movement then straight */\r
10036     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10037        mid.x = start.x + (finish.x - start.x) / 2;\r
10038        mid.y = finish.y;\r
10039      } else {\r
10040        mid.x = finish.x;\r
10041        mid.y = start.y + (finish.y - start.y) / 2;\r
10042      }\r
10043   }\r
10044   \r
10045   /* Don't use as many frames for very short moves */\r
10046   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10047     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10048   else\r
10049     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10050 \r
10051   animInfo.from.x = fromX;\r
10052   animInfo.from.y = fromY;\r
10053   animInfo.to.x = toX;\r
10054   animInfo.to.y = toY;\r
10055   animInfo.lastpos = start;\r
10056   animInfo.piece = piece;\r
10057   for (n = 0; n < nFrames; n++) {\r
10058     animInfo.pos = frames[n];\r
10059     DrawPosition(FALSE, NULL);\r
10060     animInfo.lastpos = animInfo.pos;\r
10061     Sleep(appData.animSpeed);\r
10062   }\r
10063   animInfo.pos = finish;\r
10064   DrawPosition(FALSE, NULL);\r
10065   animInfo.piece = EmptySquare;\r
10066 }\r
10067 \r
10068 /*      Convert board position to corner of screen rect and color       */\r
10069 \r
10070 static void\r
10071 ScreenSquare(column, row, pt)\r
10072      int column; int row; POINT * pt;\r
10073 {\r
10074   if (flipView) {\r
10075     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10076     pt->y = lineGap + row * (squareSize + lineGap);\r
10077   } else {\r
10078     pt->x = lineGap + column * (squareSize + lineGap);\r
10079     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10080   }\r
10081 }\r
10082 \r
10083 /*      Generate a series of frame coords from start->mid->finish.\r
10084         The movement rate doubles until the half way point is\r
10085         reached, then halves back down to the final destination,\r
10086         which gives a nice slow in/out effect. The algorithmn\r
10087         may seem to generate too many intermediates for short\r
10088         moves, but remember that the purpose is to attract the\r
10089         viewers attention to the piece about to be moved and\r
10090         then to where it ends up. Too few frames would be less\r
10091         noticeable.                                             */\r
10092 \r
10093 static void\r
10094 Tween(start, mid, finish, factor, frames, nFrames)\r
10095      POINT * start; POINT * mid;\r
10096      POINT * finish; int factor;\r
10097      POINT frames[]; int * nFrames;\r
10098 {\r
10099   int n, fraction = 1, count = 0;\r
10100 \r
10101   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10102   for (n = 0; n < factor; n++)\r
10103     fraction *= 2;\r
10104   for (n = 0; n < factor; n++) {\r
10105     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10106     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10107     count ++;\r
10108     fraction = fraction / 2;\r
10109   }\r
10110   \r
10111   /* Midpoint */\r
10112   frames[count] = *mid;\r
10113   count ++;\r
10114   \r
10115   /* Slow out, stepping 1/2, then 1/4, ... */\r
10116   fraction = 2;\r
10117   for (n = 0; n < factor; n++) {\r
10118     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10119     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10120     count ++;\r
10121     fraction = fraction * 2;\r
10122   }\r
10123   *nFrames = count;\r
10124 }\r
10125 \r
10126 void\r
10127 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10128 {\r
10129 #if 0\r
10130     char buf[256];\r
10131 \r
10132     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10133         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10134 \r
10135     OutputDebugString( buf );\r
10136 #endif\r
10137 \r
10138     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10139 \r
10140     EvalGraphSet( first, last, current, pvInfoList );\r
10141 }\r
10142 \r
10143 void SetProgramStats( FrontEndProgramStats * stats )\r
10144 {\r
10145 #if 0\r
10146     char buf[1024];\r
10147 \r
10148     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10149         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10150 \r
10151     OutputDebugString( buf );\r
10152 #endif\r
10153 \r
10154     EngineOutputUpdate( stats );\r
10155 }\r