changes from H.G. Muller; version 4.3.14
[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 PopUpNameDialog(char firstchar);\r
392 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
393 \r
394 /* [AS] */\r
395 int NewGameFRC();\r
396 int GameListOptions();\r
397 \r
398 HWND moveHistoryDialog = NULL;\r
399 BOOLEAN moveHistoryDialogUp = FALSE;\r
400 \r
401 WindowPlacement wpMoveHistory;\r
402 \r
403 HWND evalGraphDialog = NULL;\r
404 BOOLEAN evalGraphDialogUp = FALSE;\r
405 \r
406 WindowPlacement wpEvalGraph;\r
407 \r
408 HWND engineOutputDialog = NULL;\r
409 BOOLEAN engineOutputDialogUp = FALSE;\r
410 \r
411 WindowPlacement wpEngineOutput;\r
412 \r
413 VOID MoveHistoryPopUp();\r
414 VOID MoveHistoryPopDown();\r
415 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
416 BOOL MoveHistoryIsUp();\r
417 \r
418 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
419 VOID EvalGraphPopUp();\r
420 VOID EvalGraphPopDown();\r
421 BOOL EvalGraphIsUp();\r
422 \r
423 VOID EngineOutputPopUp();\r
424 VOID EngineOutputPopDown();\r
425 BOOL EngineOutputIsUp();\r
426 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
427 \r
428 VOID GothicPopUp(char *title, VariantClass variant);\r
429 /*\r
430  * Setting "frozen" should disable all user input other than deleting\r
431  * the window.  We do this while engines are initializing themselves.\r
432  */\r
433 static int frozen = 0;\r
434 static int oldMenuItemState[MENU_BAR_ITEMS];\r
435 void FreezeUI()\r
436 {\r
437   HMENU hmenu;\r
438   int i;\r
439 \r
440   if (frozen) return;\r
441   frozen = 1;\r
442   hmenu = GetMenu(hwndMain);\r
443   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
444     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
445   }\r
446   DrawMenuBar(hwndMain);\r
447 }\r
448 \r
449 /* Undo a FreezeUI */\r
450 void ThawUI()\r
451 {\r
452   HMENU hmenu;\r
453   int i;\r
454 \r
455   if (!frozen) return;\r
456   frozen = 0;\r
457   hmenu = GetMenu(hwndMain);\r
458   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
459     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
460   }\r
461   DrawMenuBar(hwndMain);\r
462 }\r
463 \r
464 /*---------------------------------------------------------------------------*\\r
465  *\r
466  * WinMain\r
467  *\r
468 \*---------------------------------------------------------------------------*/\r
469 \r
470 int APIENTRY\r
471 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
472         LPSTR lpCmdLine, int nCmdShow)\r
473 {\r
474   MSG msg;\r
475   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
476 \r
477   debugFP = stderr;\r
478 \r
479   LoadLibrary("RICHED32.DLL");\r
480   consoleCF.cbSize = sizeof(CHARFORMAT);\r
481 \r
482   if (!InitApplication(hInstance)) {\r
483     return (FALSE);\r
484   }\r
485   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
486     return (FALSE);\r
487   }\r
488 \r
489   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
490   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
491   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
492 \r
493   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
494 \r
495   while (GetMessage(&msg, /* message structure */\r
496                     NULL, /* handle of window receiving the message */\r
497                     0,    /* lowest message to examine */\r
498                     0))   /* highest message to examine */\r
499     {\r
500       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
501           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
502           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
503           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
504           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
505           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
506           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
507           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
508           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
509           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
510         TranslateMessage(&msg); /* Translates virtual key codes */\r
511         DispatchMessage(&msg);  /* Dispatches message to window */\r
512       }\r
513     }\r
514 \r
515 \r
516   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
517 }\r
518 \r
519 /*---------------------------------------------------------------------------*\\r
520  *\r
521  * Initialization functions\r
522  *\r
523 \*---------------------------------------------------------------------------*/\r
524 \r
525 BOOL\r
526 InitApplication(HINSTANCE hInstance)\r
527 {\r
528   WNDCLASS wc;\r
529 \r
530   /* Fill in window class structure with parameters that describe the */\r
531   /* main window. */\r
532 \r
533   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
534   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
535   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
536   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
537   wc.hInstance     = hInstance;         /* Owner of this class */\r
538   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
539   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
540   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
541   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
542   wc.lpszClassName = szAppName;                 /* Name to register as */\r
543 \r
544   /* Register the window class and return success/failure code. */\r
545   if (!RegisterClass(&wc)) return FALSE;\r
546 \r
547   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
548   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
549   wc.cbClsExtra    = 0;\r
550   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
551   wc.hInstance     = hInstance;\r
552   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
553   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
554   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
555   wc.lpszMenuName  = NULL;\r
556   wc.lpszClassName = szConsoleName;\r
557 \r
558   if (!RegisterClass(&wc)) return FALSE;\r
559   return TRUE;\r
560 }\r
561 \r
562 \r
563 /* Set by InitInstance, used by EnsureOnScreen */\r
564 int screenHeight, screenWidth;\r
565 \r
566 void\r
567 EnsureOnScreen(int *x, int *y)\r
568 {\r
569   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
570   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
571   if (*x > screenWidth - 32) *x = 0;\r
572   if (*y > screenHeight - 32) *y = 0;\r
573   if (*x < 10) *x = 10;\r
574   if (*y < gap) *y = gap;\r
575 }\r
576 \r
577 BOOL\r
578 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
579 {\r
580   HWND hwnd; /* Main window handle. */\r
581   int ibs;\r
582   WINDOWPLACEMENT wp;\r
583   char *filepart;\r
584 \r
585   hInst = hInstance;    /* Store instance handle in our global variable */\r
586 \r
587   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
588     *filepart = NULLCHAR;\r
589   } else {\r
590     GetCurrentDirectory(MSG_SIZ, installDir);\r
591   }\r
592   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
593   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
594   if (appData.debugMode) {\r
595     debugFP = fopen(appData.nameOfDebugFile, "w");\r
596     setbuf(debugFP, NULL);\r
597   }\r
598 \r
599   InitBackEnd1();\r
600 \r
601   InitEngineUCI( installDir, &first );\r
602   InitEngineUCI( installDir, &second );\r
603 \r
604   /* Create a main window for this application instance. */\r
605   hwnd = CreateWindow(szAppName, szTitle,\r
606                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
607                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
608                       NULL, NULL, hInstance, NULL);\r
609   hwndMain = hwnd;\r
610 \r
611   /* If window could not be created, return "failure" */\r
612   if (!hwnd) {\r
613     return (FALSE);\r
614   }\r
615 \r
616   iconWhite = LoadIcon(hInstance, "icon_white");\r
617   iconBlack = LoadIcon(hInstance, "icon_black");\r
618   iconCurrent = iconWhite;\r
619   InitDrawingColors();\r
620   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
621   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
622   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
623     /* Compute window size for each board size, and use the largest\r
624        size that fits on this screen as the default. */\r
625     InitDrawingSizes((BoardSize)ibs, 0);\r
626     if (boardSize == (BoardSize)-1 &&\r
627         winHeight <= screenHeight\r
628            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
629         && winWidth <= screenWidth) {\r
630       boardSize = (BoardSize)ibs;\r
631     }\r
632   }\r
633   InitDrawingSizes(boardSize, 0);\r
634   InitMenuChecks();\r
635   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
636 \r
637   /* [AS] Load textures if specified */\r
638   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
639   \r
640   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
641       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642       liteBackTextureMode = appData.liteBackTextureMode;\r
643 \r
644       if (liteBackTexture == NULL && appData.debugMode) {\r
645           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
646       }\r
647   }\r
648   \r
649   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
650       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
651       darkBackTextureMode = appData.darkBackTextureMode;\r
652 \r
653       if (darkBackTexture == NULL && appData.debugMode) {\r
654           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
655       }\r
656   }\r
657 \r
658   mysrandom( (unsigned) time(NULL) );\r
659 \r
660   /* Make a console window if needed */\r
661   if (appData.icsActive) {\r
662     ConsoleCreate();\r
663   }\r
664 \r
665   /* [AS] Restore layout */\r
666   if( wpMoveHistory.visible ) {\r
667       MoveHistoryPopUp();\r
668   }\r
669 \r
670   if( wpEvalGraph.visible ) {\r
671       EvalGraphPopUp();\r
672   }\r
673 \r
674   if( wpEngineOutput.visible ) {\r
675       EngineOutputPopUp();\r
676   }\r
677 \r
678   InitBackEnd2();\r
679 \r
680   /* Make the window visible; update its client area; and return "success" */\r
681   EnsureOnScreen(&boardX, &boardY);\r
682   wp.length = sizeof(WINDOWPLACEMENT);\r
683   wp.flags = 0;\r
684   wp.showCmd = nCmdShow;\r
685   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
686   wp.rcNormalPosition.left = boardX;\r
687   wp.rcNormalPosition.right = boardX + winWidth;\r
688   wp.rcNormalPosition.top = boardY;\r
689   wp.rcNormalPosition.bottom = boardY + winHeight;\r
690   SetWindowPlacement(hwndMain, &wp);\r
691 \r
692   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
693                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
694 \r
695 #if 0\r
696   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
697   if( gameInfo.variant != VariantFischeRandom ) {\r
698       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
699   }\r
700 #endif\r
701   if (hwndConsole) {\r
702 #if AOT_CONSOLE\r
703     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
704                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
705 #endif\r
706     ShowWindow(hwndConsole, nCmdShow);\r
707   }\r
708   UpdateWindow(hwnd);\r
709 \r
710   return TRUE;\r
711 \r
712 }\r
713 \r
714 \r
715 typedef enum {\r
716   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
717   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
718   ArgSettingsFilename\r
719 } ArgType;\r
720 \r
721 typedef struct {\r
722   char *argName;\r
723   ArgType argType;\r
724   /***\r
725   union {\r
726     String *pString;       // ArgString\r
727     int *pInt;             // ArgInt\r
728     float *pFloat;         // ArgFloat\r
729     Boolean *pBoolean;     // ArgBoolean\r
730     COLORREF *pColor;      // ArgColor\r
731     ColorClass cc;         // ArgAttribs\r
732     String *pFilename;     // ArgFilename\r
733     BoardSize *pBoardSize; // ArgBoardSize\r
734     int whichFont;         // ArgFont\r
735     DCB *pDCB;             // ArgCommSettings\r
736     String *pFilename;     // ArgSettingsFilename\r
737   } argLoc;\r
738   ***/\r
739   LPVOID argLoc;\r
740   BOOL save;\r
741 } ArgDescriptor;\r
742 \r
743 int junk;\r
744 ArgDescriptor argDescriptors[] = {\r
745   /* positional arguments */\r
746   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
747   { "", ArgNone, NULL },\r
748   /* keyword arguments */\r
749   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
750   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
751   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
752   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
753   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
754   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
755   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
756   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
757   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
758   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
759   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
760   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
761   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
762   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
763   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
764   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
765   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
766   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
767     FALSE },\r
768   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
769     FALSE },\r
770   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
771     FALSE },\r
772   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
773   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
774     FALSE },\r
775   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
776   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
779   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
780   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
783   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
784   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
785   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
786   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
787   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
788   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
789   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
790   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
791   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
792   /*!!bitmapDirectory?*/\r
793   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
794   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
795   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
796   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
797   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
798   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
799   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
800   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
801   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
802   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
803   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
804   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
805   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
806   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
807   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
808   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
809   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
810   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
811   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
814   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
815   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
816   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
817   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
820   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
821   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
822   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
823   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
824   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
825   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
826   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
827   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
828   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
829   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
830   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
831   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
833   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
834   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
835   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
836   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
837   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
838   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
839   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
840   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
841   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
842   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
843   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
844   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
845   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
846   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
847   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
848   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
849   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
850   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
851   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
852   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
853   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
854   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
855   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
856   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
857   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
858   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
859   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
860   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
861   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
862   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
863   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
864   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
865   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
866   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
867   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
868   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
869   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
870   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
871   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
873   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
874   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
875   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
877   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
878   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
879   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
881   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
882   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
883   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
885   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
886   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
887     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
888   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
889   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
890   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
891   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
892   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
893   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
894   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
895   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
896     TRUE }, /* must come after all fonts */\r
897   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
898   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
899     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
900   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
901   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
903   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
904   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
905   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
907   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
908   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
909   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
910   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
911   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
912   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
913   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
915   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
916   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
917   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
918   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
919   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
920   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
921   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
923   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
924   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
925   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
927   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
928 #if 0\r
929   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
930   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
931 #endif\r
932   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
933   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
935   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
936   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
937   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
939   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
940   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
941   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
943   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
944   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
945   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
947   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
948   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
949   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
950   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
951   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
952   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
953   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
954   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
955   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
956   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
957   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
959   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
960   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
961   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
962   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
964   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
965   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
966   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
967   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
969   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
970   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
971   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
972   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
973   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
975   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
976   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
977   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
978   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
979   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
980   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
981   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
982   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
984   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
985   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
986   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
987   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
988   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
989   { "highlightLastMove", ArgBoolean,\r
990     (LPVOID) &appData.highlightLastMove, TRUE },\r
991   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
993   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
994   { "highlightDragging", ArgBoolean,\r
995     (LPVOID) &appData.highlightDragging, TRUE },\r
996   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
998   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
999   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1000   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1001   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1002   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1003   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1004   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1005   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1006   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1007   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1008   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1009   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1010   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1011   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1012   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1013   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1014   { "soundShout", ArgFilename,\r
1015     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1016   { "soundSShout", ArgFilename,\r
1017     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1018   { "soundChannel1", ArgFilename,\r
1019     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1020   { "soundChannel", ArgFilename,\r
1021     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1022   { "soundKibitz", ArgFilename,\r
1023     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1024   { "soundTell", ArgFilename,\r
1025     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1026   { "soundChallenge", ArgFilename,\r
1027     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1028   { "soundRequest", ArgFilename,\r
1029     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1030   { "soundSeek", ArgFilename,\r
1031     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1032   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1033   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1034   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1035   { "soundIcsLoss", ArgFilename, \r
1036     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1037   { "soundIcsDraw", ArgFilename, \r
1038     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1039   { "soundIcsUnfinished", ArgFilename, \r
1040     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1041   { "soundIcsAlarm", ArgFilename, \r
1042     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1043   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1046   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1047   { "reuseChessPrograms", ArgBoolean,\r
1048     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1049   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1052   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1053   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1054   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1055   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1056   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1057   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1058   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1059   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1060   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1061   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1062   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1063   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1064   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1065   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1066   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1067   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1068   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1069   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1070   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1071   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1072   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1073   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1074   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1075   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1076   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1077   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1078   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1079   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1080   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1081   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1082   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1083   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1084   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1085   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1086     TRUE },\r
1087   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1088     TRUE },\r
1089   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1090   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1091   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1092   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1093   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1094   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1095   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1097   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1098   /* [AS] New features */\r
1099   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1100   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1101   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1102   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1103   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1104   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1105   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1106   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1107   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1108   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1109   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1110   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1111   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1112   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1113   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1114   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1115   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1116   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1117   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1118   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1119   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1120   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1121   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1122   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1123   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1124   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1125   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1126   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1127   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1128   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1129   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1130   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1131   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1132   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1133   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1134   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1135   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1136   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1137   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1138   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1139   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1140   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1141   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1142   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1143   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1144   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1145   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1146   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1147 \r
1148   /* [AS] Layout stuff */\r
1149   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1150   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1151   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1152   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1153   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1154 \r
1155   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1156   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1157   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1158   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1159   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1160 \r
1161   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1162   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1163   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1164   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1165   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1166 \r
1167   /* [HGM] board-size, adjudication and misc. options */\r
1168   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1169   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1170   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1171   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1172   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1173   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1174   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1175   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1176   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1177   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1178   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1179   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1180   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1181   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1182   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1183   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1184   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1185 \r
1186 #ifdef ZIPPY\r
1187   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1188   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1189   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1190   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1191   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1192   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1193   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1194   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1195   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1196   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1197   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1198   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1199   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1200     FALSE },\r
1201   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1202   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1203   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1204   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1205   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1206   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1207   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1208     FALSE },\r
1209   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1210   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1211   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1212   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1213   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1214   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1215   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1216   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1217   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1218   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1219   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1220   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1221   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1222   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1223   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1224   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1225   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1226   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1227 #endif\r
1228   /* [HGM] options for broadcasting and time odds */\r
1229   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1230   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1231   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1232   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1233   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1234   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1235   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1236   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1237   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1238   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1239   { NULL, ArgNone, NULL, FALSE }\r
1240 };\r
1241 \r
1242 \r
1243 /* Kludge for indirection files on command line */\r
1244 char* lastIndirectionFilename;\r
1245 ArgDescriptor argDescriptorIndirection =\r
1246 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1247 \r
1248 \r
1249 VOID\r
1250 ExitArgError(char *msg, char *badArg)\r
1251 {\r
1252   char buf[MSG_SIZ];\r
1253 \r
1254   sprintf(buf, "%s %s", msg, badArg);\r
1255   DisplayFatalError(buf, 0, 2);\r
1256   exit(2);\r
1257 }\r
1258 \r
1259 /* Command line font name parser.  NULL name means do nothing.\r
1260    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1261    For backward compatibility, syntax without the colon is also\r
1262    accepted, but font names with digits in them won't work in that case.\r
1263 */\r
1264 VOID\r
1265 ParseFontName(char *name, MyFontParams *mfp)\r
1266 {\r
1267   char *p, *q;\r
1268   if (name == NULL) return;\r
1269   p = name;\r
1270   q = strchr(p, ':');\r
1271   if (q) {\r
1272     if (q - p >= sizeof(mfp->faceName))\r
1273       ExitArgError("Font name too long:", name);\r
1274     memcpy(mfp->faceName, p, q - p);\r
1275     mfp->faceName[q - p] = NULLCHAR;\r
1276     p = q + 1;\r
1277   } else {\r
1278     q = mfp->faceName;\r
1279     while (*p && !isdigit(*p)) {\r
1280       *q++ = *p++;\r
1281       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1282         ExitArgError("Font name too long:", name);\r
1283     }\r
1284     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1285     *q = NULLCHAR;\r
1286   }\r
1287   if (!*p) ExitArgError("Font point size missing:", name);\r
1288   mfp->pointSize = (float) atof(p);\r
1289   mfp->bold = (strchr(p, 'b') != NULL);\r
1290   mfp->italic = (strchr(p, 'i') != NULL);\r
1291   mfp->underline = (strchr(p, 'u') != NULL);\r
1292   mfp->strikeout = (strchr(p, 's') != NULL);\r
1293 }\r
1294 \r
1295 /* Color name parser.\r
1296    X version accepts X color names, but this one\r
1297    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1298 COLORREF\r
1299 ParseColorName(char *name)\r
1300 {\r
1301   int red, green, blue, count;\r
1302   char buf[MSG_SIZ];\r
1303 \r
1304   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1305   if (count != 3) {\r
1306     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1307       &red, &green, &blue);\r
1308   }\r
1309   if (count != 3) {\r
1310     sprintf(buf, "Can't parse color name %s", name);\r
1311     DisplayError(buf, 0);\r
1312     return RGB(0, 0, 0);\r
1313   }\r
1314   return PALETTERGB(red, green, blue);\r
1315 }\r
1316 \r
1317 \r
1318 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1319 {\r
1320   char *e = argValue;\r
1321   int eff = 0;\r
1322 \r
1323   while (*e) {\r
1324     if (*e == 'b')      eff |= CFE_BOLD;\r
1325     else if (*e == 'i') eff |= CFE_ITALIC;\r
1326     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1327     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1328     else if (*e == '#' || isdigit(*e)) break;\r
1329     e++;\r
1330   }\r
1331   *effects = eff;\r
1332   *color   = ParseColorName(e);\r
1333 }\r
1334 \r
1335 \r
1336 BoardSize\r
1337 ParseBoardSize(char *name)\r
1338 {\r
1339   BoardSize bs = SizeTiny;\r
1340   while (sizeInfo[bs].name != NULL) {\r
1341     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1342     bs++;\r
1343   }\r
1344   ExitArgError("Unrecognized board size value", name);\r
1345   return bs; /* not reached */\r
1346 }\r
1347 \r
1348 \r
1349 char\r
1350 StringGet(void *getClosure)\r
1351 {\r
1352   char **p = (char **) getClosure;\r
1353   return *((*p)++);\r
1354 }\r
1355 \r
1356 char\r
1357 FileGet(void *getClosure)\r
1358 {\r
1359   int c;\r
1360   FILE* f = (FILE*) getClosure;\r
1361 \r
1362   c = getc(f);\r
1363   if (c == EOF)\r
1364     return NULLCHAR;\r
1365   else\r
1366     return (char) c;\r
1367 }\r
1368 \r
1369 /* Parse settings file named "name". If file found, return the\r
1370    full name in fullname and return TRUE; else return FALSE */\r
1371 BOOLEAN\r
1372 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1373 {\r
1374   char *dummy;\r
1375   FILE *f;\r
1376 \r
1377   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1378     f = fopen(fullname, "r");\r
1379     if (f != NULL) {\r
1380       ParseArgs(FileGet, f);\r
1381       fclose(f);\r
1382       return TRUE;\r
1383     }\r
1384   }\r
1385   return FALSE;\r
1386 }\r
1387 \r
1388 VOID\r
1389 ParseArgs(GetFunc get, void *cl)\r
1390 {\r
1391   char argName[ARG_MAX];\r
1392   char argValue[ARG_MAX];\r
1393   ArgDescriptor *ad;\r
1394   char start;\r
1395   char *q;\r
1396   int i, octval;\r
1397   char ch;\r
1398   int posarg = 0;\r
1399 \r
1400   ch = get(cl);\r
1401   for (;;) {\r
1402     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1403     if (ch == NULLCHAR) break;\r
1404     if (ch == ';') {\r
1405       /* Comment to end of line */\r
1406       ch = get(cl);\r
1407       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1408       continue;\r
1409     } else if (ch == '/' || ch == '-') {\r
1410       /* Switch */\r
1411       q = argName;\r
1412       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1413              ch != '\n' && ch != '\t') {\r
1414         *q++ = ch;\r
1415         ch = get(cl);\r
1416       }\r
1417       *q = NULLCHAR;\r
1418 \r
1419       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1420         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1421 \r
1422       if (ad->argName == NULL)\r
1423         ExitArgError("Unrecognized argument", argName);\r
1424 \r
1425     } else if (ch == '@') {\r
1426       /* Indirection file */\r
1427       ad = &argDescriptorIndirection;\r
1428       ch = get(cl);\r
1429     } else {\r
1430       /* Positional argument */\r
1431       ad = &argDescriptors[posarg++];\r
1432       strcpy(argName, ad->argName);\r
1433     }\r
1434 \r
1435     if (ad->argType == ArgTrue) {\r
1436       *(Boolean *) ad->argLoc = TRUE;\r
1437       continue;\r
1438     }\r
1439     if (ad->argType == ArgFalse) {\r
1440       *(Boolean *) ad->argLoc = FALSE;\r
1441       continue;\r
1442     }\r
1443 \r
1444     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1445     if (ch == NULLCHAR || ch == '\n') {\r
1446       ExitArgError("No value provided for argument", argName);\r
1447     }\r
1448     q = argValue;\r
1449     if (ch == '{') {\r
1450       // Quoting with { }.  No characters have to (or can) be escaped.\r
1451       // Thus the string cannot contain a '}' character.\r
1452       start = ch;\r
1453       ch = get(cl);\r
1454       while (start) {\r
1455         switch (ch) {\r
1456         case NULLCHAR:\r
1457           start = NULLCHAR;\r
1458           break;\r
1459           \r
1460         case '}':\r
1461           ch = get(cl);\r
1462           start = NULLCHAR;\r
1463           break;\r
1464 \r
1465         default:\r
1466           *q++ = ch;\r
1467           ch = get(cl);\r
1468           break;\r
1469         }\r
1470       }   \r
1471     } else if (ch == '\'' || ch == '"') {\r
1472       // Quoting with ' ' or " ", with \ as escape character.\r
1473       // Inconvenient for long strings that may contain Windows filenames.\r
1474       start = ch;\r
1475       ch = get(cl);\r
1476       while (start) {\r
1477         switch (ch) {\r
1478         case NULLCHAR:\r
1479           start = NULLCHAR;\r
1480           break;\r
1481 \r
1482         default:\r
1483         not_special:\r
1484           *q++ = ch;\r
1485           ch = get(cl);\r
1486           break;\r
1487 \r
1488         case '\'':\r
1489         case '\"':\r
1490           if (ch == start) {\r
1491             ch = get(cl);\r
1492             start = NULLCHAR;\r
1493             break;\r
1494           } else {\r
1495             goto not_special;\r
1496           }\r
1497 \r
1498         case '\\':\r
1499           if (ad->argType == ArgFilename\r
1500               || ad->argType == ArgSettingsFilename) {\r
1501               goto not_special;\r
1502           }\r
1503           ch = get(cl);\r
1504           switch (ch) {\r
1505           case NULLCHAR:\r
1506             ExitArgError("Incomplete \\ escape in value for", argName);\r
1507             break;\r
1508           case 'n':\r
1509             *q++ = '\n';\r
1510             ch = get(cl);\r
1511             break;\r
1512           case 'r':\r
1513             *q++ = '\r';\r
1514             ch = get(cl);\r
1515             break;\r
1516           case 't':\r
1517             *q++ = '\t';\r
1518             ch = get(cl);\r
1519             break;\r
1520           case 'b':\r
1521             *q++ = '\b';\r
1522             ch = get(cl);\r
1523             break;\r
1524           case 'f':\r
1525             *q++ = '\f';\r
1526             ch = get(cl);\r
1527             break;\r
1528           default:\r
1529             octval = 0;\r
1530             for (i = 0; i < 3; i++) {\r
1531               if (ch >= '0' && ch <= '7') {\r
1532                 octval = octval*8 + (ch - '0');\r
1533                 ch = get(cl);\r
1534               } else {\r
1535                 break;\r
1536               }\r
1537             }\r
1538             if (i > 0) {\r
1539               *q++ = (char) octval;\r
1540             } else {\r
1541               *q++ = ch;\r
1542               ch = get(cl);\r
1543             }\r
1544             break;\r
1545           }\r
1546           break;\r
1547         }\r
1548       }\r
1549     } else {\r
1550       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1551         *q++ = ch;\r
1552         ch = get(cl);\r
1553       }\r
1554     }\r
1555     *q = NULLCHAR;\r
1556 \r
1557     switch (ad->argType) {\r
1558     case ArgInt:\r
1559       *(int *) ad->argLoc = atoi(argValue);\r
1560       break;\r
1561 \r
1562     case ArgFloat:\r
1563       *(float *) ad->argLoc = (float) atof(argValue);\r
1564       break;\r
1565 \r
1566     case ArgString:\r
1567     case ArgFilename:\r
1568       *(char **) ad->argLoc = strdup(argValue);\r
1569       break;\r
1570 \r
1571     case ArgSettingsFilename:\r
1572       {\r
1573         char fullname[MSG_SIZ];\r
1574         if (ParseSettingsFile(argValue, fullname)) {\r
1575           if (ad->argLoc != NULL) {\r
1576             *(char **) ad->argLoc = strdup(fullname);\r
1577           }\r
1578         } else {\r
1579           if (ad->argLoc != NULL) {\r
1580           } else {\r
1581             ExitArgError("Failed to open indirection file", argValue);\r
1582           }\r
1583         }\r
1584       }\r
1585       break;\r
1586 \r
1587     case ArgBoolean:\r
1588       switch (argValue[0]) {\r
1589       case 't':\r
1590       case 'T':\r
1591         *(Boolean *) ad->argLoc = TRUE;\r
1592         break;\r
1593       case 'f':\r
1594       case 'F':\r
1595         *(Boolean *) ad->argLoc = FALSE;\r
1596         break;\r
1597       default:\r
1598         ExitArgError("Unrecognized boolean argument value", argValue);\r
1599         break;\r
1600       }\r
1601       break;\r
1602 \r
1603     case ArgColor:\r
1604       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1605       break;\r
1606 \r
1607     case ArgAttribs: {\r
1608       ColorClass cc = (ColorClass)ad->argLoc;\r
1609       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1610       }\r
1611       break;\r
1612       \r
1613     case ArgBoardSize:\r
1614       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1615       break;\r
1616 \r
1617     case ArgFont:\r
1618       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1619       break;\r
1620 \r
1621     case ArgCommSettings:\r
1622       ParseCommSettings(argValue, &dcb);\r
1623       break;\r
1624 \r
1625     case ArgNone:\r
1626       ExitArgError("Unrecognized argument", argValue);\r
1627       break;\r
1628     }\r
1629   }\r
1630 }\r
1631 \r
1632 VOID\r
1633 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1634 {\r
1635   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1636   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1637   DeleteDC(hdc);\r
1638   lf->lfWidth = 0;\r
1639   lf->lfEscapement = 0;\r
1640   lf->lfOrientation = 0;\r
1641   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1642   lf->lfItalic = mfp->italic;\r
1643   lf->lfUnderline = mfp->underline;\r
1644   lf->lfStrikeOut = mfp->strikeout;\r
1645   lf->lfCharSet = DEFAULT_CHARSET;\r
1646   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1647   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1648   lf->lfQuality = DEFAULT_QUALITY;\r
1649   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1650   strcpy(lf->lfFaceName, mfp->faceName);\r
1651 }\r
1652 \r
1653 VOID\r
1654 CreateFontInMF(MyFont *mf)\r
1655 {\r
1656   LFfromMFP(&mf->lf, &mf->mfp);\r
1657   if (mf->hf) DeleteObject(mf->hf);\r
1658   mf->hf = CreateFontIndirect(&mf->lf);\r
1659 }\r
1660 \r
1661 VOID\r
1662 SetDefaultTextAttribs()\r
1663 {\r
1664   ColorClass cc;\r
1665   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1666     ParseAttribs(&textAttribs[cc].color, \r
1667                  &textAttribs[cc].effects, \r
1668                  defaultTextAttribs[cc]);\r
1669   }\r
1670 }\r
1671 \r
1672 VOID\r
1673 SetDefaultSounds()\r
1674 {\r
1675   ColorClass cc;\r
1676   SoundClass sc;\r
1677   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1678     textAttribs[cc].sound.name = strdup("");\r
1679     textAttribs[cc].sound.data = NULL;\r
1680   }\r
1681   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1682     sounds[sc].name = strdup("");\r
1683     sounds[sc].data = NULL;\r
1684   }\r
1685   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1686 }\r
1687 \r
1688 VOID\r
1689 LoadAllSounds()\r
1690 {\r
1691   ColorClass cc;\r
1692   SoundClass sc;\r
1693   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1694     MyLoadSound(&textAttribs[cc].sound);\r
1695   }\r
1696   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1697     MyLoadSound(&sounds[sc]);\r
1698   }\r
1699 }\r
1700 \r
1701 VOID\r
1702 InitAppData(LPSTR lpCmdLine)\r
1703 {\r
1704   int i, j;\r
1705   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1706   char *dummy, *p;\r
1707 \r
1708   programName = szAppName;\r
1709 \r
1710   /* Initialize to defaults */\r
1711   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1712   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1713   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1714   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1715   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1716   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1717   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1718   SetDefaultTextAttribs();\r
1719   SetDefaultSounds();\r
1720   appData.movesPerSession = MOVES_PER_SESSION;\r
1721   appData.initString = INIT_STRING;\r
1722   appData.secondInitString = INIT_STRING;\r
1723   appData.firstComputerString = COMPUTER_STRING;\r
1724   appData.secondComputerString = COMPUTER_STRING;\r
1725   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1726   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1727   appData.firstPlaysBlack = FALSE;\r
1728   appData.noChessProgram = FALSE;\r
1729   chessProgram = FALSE;\r
1730   appData.firstHost = FIRST_HOST;\r
1731   appData.secondHost = SECOND_HOST;\r
1732   appData.firstDirectory = FIRST_DIRECTORY;\r
1733   appData.secondDirectory = SECOND_DIRECTORY;\r
1734   appData.bitmapDirectory = "";\r
1735   appData.remoteShell = REMOTE_SHELL;\r
1736   appData.remoteUser = "";\r
1737   appData.timeDelay = TIME_DELAY;\r
1738   appData.timeControl = TIME_CONTROL;\r
1739   appData.timeIncrement = TIME_INCREMENT;\r
1740   appData.icsActive = FALSE;\r
1741   appData.icsHost = "";\r
1742   appData.icsPort = ICS_PORT;\r
1743   appData.icsCommPort = ICS_COMM_PORT;\r
1744   appData.icsLogon = ICS_LOGON;\r
1745   appData.icsHelper = "";\r
1746   appData.useTelnet = FALSE;\r
1747   appData.telnetProgram = TELNET_PROGRAM;\r
1748   appData.gateway = "";\r
1749   appData.loadGameFile = "";\r
1750   appData.loadGameIndex = 0;\r
1751   appData.saveGameFile = "";\r
1752   appData.autoSaveGames = FALSE;\r
1753   appData.loadPositionFile = "";\r
1754   appData.loadPositionIndex = 1;\r
1755   appData.savePositionFile = "";\r
1756   appData.matchMode = FALSE;\r
1757   appData.matchGames = 0;\r
1758   appData.monoMode = FALSE;\r
1759   appData.debugMode = FALSE;\r
1760   appData.clockMode = TRUE;\r
1761   boardSize = (BoardSize) -1; /* determine by screen size */\r
1762   appData.Iconic = FALSE; /*unused*/\r
1763   appData.searchTime = "";\r
1764   appData.searchDepth = 0;\r
1765   appData.showCoords = FALSE;\r
1766   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1767   appData.autoCallFlag = FALSE;\r
1768   appData.flipView = FALSE;\r
1769   appData.autoFlipView = TRUE;\r
1770   appData.cmailGameName = "";\r
1771   appData.alwaysPromoteToQueen = FALSE;\r
1772   appData.oldSaveStyle = FALSE;\r
1773   appData.quietPlay = FALSE;\r
1774   appData.showThinking = FALSE;\r
1775   appData.ponderNextMove = TRUE;\r
1776   appData.periodicUpdates = TRUE;\r
1777   appData.popupExitMessage = TRUE;\r
1778   appData.popupMoveErrors = FALSE;\r
1779   appData.autoObserve = FALSE;\r
1780   appData.autoComment = FALSE;\r
1781   appData.animate = TRUE;\r
1782   appData.animSpeed = 10;\r
1783   appData.animateDragging = TRUE;\r
1784   appData.highlightLastMove = TRUE;\r
1785   appData.getMoveList = TRUE;\r
1786   appData.testLegality = TRUE;\r
1787   appData.premove = TRUE;\r
1788   appData.premoveWhite = FALSE;\r
1789   appData.premoveWhiteText = "";\r
1790   appData.premoveBlack = FALSE;\r
1791   appData.premoveBlackText = "";\r
1792   appData.icsAlarm = TRUE;\r
1793   appData.icsAlarmTime = 5000;\r
1794   appData.autoRaiseBoard = TRUE;\r
1795   appData.localLineEditing = TRUE;\r
1796   appData.colorize = TRUE;\r
1797   appData.reuseFirst = TRUE;\r
1798   appData.reuseSecond = TRUE;\r
1799   appData.blindfold = FALSE;\r
1800   dcb.DCBlength = sizeof(DCB);\r
1801   dcb.BaudRate = 9600;\r
1802   dcb.fBinary = TRUE;\r
1803   dcb.fParity = FALSE;\r
1804   dcb.fOutxCtsFlow = FALSE;\r
1805   dcb.fOutxDsrFlow = FALSE;\r
1806   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1807   dcb.fDsrSensitivity = FALSE;\r
1808   dcb.fTXContinueOnXoff = TRUE;\r
1809   dcb.fOutX = FALSE;\r
1810   dcb.fInX = FALSE;\r
1811   dcb.fNull = FALSE;\r
1812   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1813   dcb.fAbortOnError = FALSE;\r
1814   dcb.wReserved = 0;\r
1815   dcb.ByteSize = 7;\r
1816   dcb.Parity = SPACEPARITY;\r
1817   dcb.StopBits = ONESTOPBIT;\r
1818   settingsFileName = SETTINGS_FILE;\r
1819   saveSettingsOnExit = TRUE;\r
1820   boardX = CW_USEDEFAULT;\r
1821   boardY = CW_USEDEFAULT;\r
1822   consoleX = CW_USEDEFAULT; \r
1823   consoleY = CW_USEDEFAULT; \r
1824   consoleW = CW_USEDEFAULT;\r
1825   consoleH = CW_USEDEFAULT;\r
1826   analysisX = CW_USEDEFAULT; \r
1827   analysisY = CW_USEDEFAULT; \r
1828   analysisW = CW_USEDEFAULT;\r
1829   analysisH = CW_USEDEFAULT;\r
1830   commentX = CW_USEDEFAULT; \r
1831   commentY = CW_USEDEFAULT; \r
1832   commentW = CW_USEDEFAULT;\r
1833   commentH = CW_USEDEFAULT;\r
1834   editTagsX = CW_USEDEFAULT; \r
1835   editTagsY = CW_USEDEFAULT; \r
1836   editTagsW = CW_USEDEFAULT;\r
1837   editTagsH = CW_USEDEFAULT;\r
1838   gameListX = CW_USEDEFAULT; \r
1839   gameListY = CW_USEDEFAULT; \r
1840   gameListW = CW_USEDEFAULT;\r
1841   gameListH = CW_USEDEFAULT;\r
1842   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1843   icsNames = ICS_NAMES;\r
1844   firstChessProgramNames = FCP_NAMES;\r
1845   secondChessProgramNames = SCP_NAMES;\r
1846   appData.initialMode = "";\r
1847   appData.variant = "normal";\r
1848   appData.firstProtocolVersion = PROTOVER;\r
1849   appData.secondProtocolVersion = PROTOVER;\r
1850   appData.showButtonBar = TRUE;\r
1851 \r
1852    /* [AS] New properties (see comments in header file) */\r
1853   appData.firstScoreIsAbsolute = FALSE;\r
1854   appData.secondScoreIsAbsolute = FALSE;\r
1855   appData.saveExtendedInfoInPGN = FALSE;\r
1856   appData.hideThinkingFromHuman = FALSE;\r
1857   appData.liteBackTextureFile = "";\r
1858   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1859   appData.darkBackTextureFile = "";\r
1860   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1861   appData.renderPiecesWithFont = "";\r
1862   appData.fontToPieceTable = "";\r
1863   appData.fontBackColorWhite = 0;\r
1864   appData.fontForeColorWhite = 0;\r
1865   appData.fontBackColorBlack = 0;\r
1866   appData.fontForeColorBlack = 0;\r
1867   appData.fontPieceSize = 80;\r
1868   appData.overrideLineGap = 1;\r
1869   appData.adjudicateLossThreshold = 0;\r
1870   appData.delayBeforeQuit = 0;\r
1871   appData.delayAfterQuit = 0;\r
1872   appData.nameOfDebugFile = "winboard.debug";\r
1873   appData.pgnEventHeader = "Computer Chess Game";\r
1874   appData.defaultFrcPosition = -1;\r
1875   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1876   appData.saveOutOfBookInfo = TRUE;\r
1877   appData.showEvalInMoveHistory = TRUE;\r
1878   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1879   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1880   appData.highlightMoveWithArrow = FALSE;\r
1881   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1882   appData.useStickyWindows = TRUE;\r
1883   appData.adjudicateDrawMoves = 0;\r
1884   appData.autoDisplayComment = TRUE;\r
1885   appData.autoDisplayTags = TRUE;\r
1886   appData.firstIsUCI = FALSE;\r
1887   appData.secondIsUCI = FALSE;\r
1888   appData.firstHasOwnBookUCI = TRUE;\r
1889   appData.secondHasOwnBookUCI = TRUE;\r
1890   appData.polyglotDir = "";\r
1891   appData.usePolyglotBook = FALSE;\r
1892   appData.polyglotBook = "";\r
1893   appData.defaultHashSize = 64;\r
1894   appData.defaultCacheSizeEGTB = 4;\r
1895   appData.defaultPathEGTB = "c:\\egtb";\r
1896 \r
1897   InitWindowPlacement( &wpMoveHistory );\r
1898   InitWindowPlacement( &wpEvalGraph );\r
1899   InitWindowPlacement( &wpEngineOutput );\r
1900 \r
1901   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1902   appData.NrFiles      = -1;\r
1903   appData.NrRanks      = -1;\r
1904   appData.holdingsSize = -1;\r
1905   appData.testClaims   = FALSE;\r
1906   appData.checkMates   = FALSE;\r
1907   appData.materialDraws= FALSE;\r
1908   appData.trivialDraws = FALSE;\r
1909   appData.ruleMoves    = 51;\r
1910   appData.drawRepeats  = 6;\r
1911   appData.matchPause   = 10000;\r
1912   appData.alphaRank    = FALSE;\r
1913   appData.allWhite     = FALSE;\r
1914   appData.upsideDown   = FALSE;\r
1915   appData.serverPause  = 15;\r
1916   appData.serverMovesName   = NULL;\r
1917   appData.suppressLoadMoves = FALSE;\r
1918   appData.firstTimeOdds  = 1;\r
1919   appData.secondTimeOdds = 1;\r
1920   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1921   appData.secondAccumulateTC = 1;\r
1922   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1923   appData.secondNPS = -1;\r
1924   appData.engineComments = 1;\r
1925 \r
1926 \r
1927 #ifdef ZIPPY\r
1928   appData.zippyTalk = ZIPPY_TALK;\r
1929   appData.zippyPlay = ZIPPY_PLAY;\r
1930   appData.zippyLines = ZIPPY_LINES;\r
1931   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1932   appData.zippyPassword = ZIPPY_PASSWORD;\r
1933   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1934   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1935   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1936   appData.zippyUseI = ZIPPY_USE_I;\r
1937   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1938   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1939   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1940   appData.zippyGameStart = ZIPPY_GAME_START;\r
1941   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1942   appData.zippyAbort = ZIPPY_ABORT;\r
1943   appData.zippyVariants = ZIPPY_VARIANTS;\r
1944   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1945   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1946 #endif\r
1947 \r
1948   /* Point font array elements to structures and\r
1949      parse default font names */\r
1950   for (i=0; i<NUM_FONTS; i++) {\r
1951     for (j=0; j<NUM_SIZES; j++) {\r
1952       font[j][i] = &fontRec[j][i];\r
1953       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1954     }\r
1955   }\r
1956   \r
1957   /* Parse default settings file if any */\r
1958   if (ParseSettingsFile(settingsFileName, buf)) {\r
1959     settingsFileName = strdup(buf);\r
1960   }\r
1961 \r
1962   /* Parse command line */\r
1963   ParseArgs(StringGet, &lpCmdLine);\r
1964 \r
1965   /* [HGM] make sure board size is acceptable */\r
1966   if(appData.NrFiles > BOARD_SIZE ||\r
1967      appData.NrRanks > BOARD_SIZE   )\r
1968       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1969 \r
1970   /* [HGM] After parsing the options from the .ini file, and overruling them\r
1971    * with options from the command line, we now make an even higher priority\r
1972    * overrule by WB options attached to the engine command line. This so that\r
1973    * tournament managers can use WB options (such as /timeOdds) that follow\r
1974    * the engines.\r
1975    */\r
1976   if(appData.firstChessProgram != NULL) {\r
1977       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
1978       static char *f = "first";\r
1979       char buf[MSG_SIZ], *q = buf;\r
1980       if(p != NULL) { // engine command line contains WinBoard options\r
1981           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
1982           ParseArgs(StringGet, &q);\r
1983           p[-1] = 0; // cut them offengine command line\r
1984       }\r
1985   }\r
1986   // now do same for second chess program\r
1987   if(appData.secondChessProgram != NULL) {\r
1988       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
1989       static char *s = "second";\r
1990       char buf[MSG_SIZ], *q = buf;\r
1991       if(p != NULL) { // engine command line contains WinBoard options\r
1992           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
1993           ParseArgs(StringGet, &q);\r
1994           p[-1] = 0; // cut them offengine command line\r
1995       }\r
1996   }\r
1997 \r
1998 \r
1999   /* Propagate options that affect others */\r
2000   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2001   if (appData.icsActive || appData.noChessProgram) {\r
2002      chessProgram = FALSE;  /* not local chess program mode */\r
2003   }\r
2004 \r
2005   /* Open startup dialog if needed */\r
2006   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2007       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2008       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2009                         *appData.secondChessProgram == NULLCHAR))) {\r
2010     FARPROC lpProc;\r
2011     \r
2012     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2013     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2014     FreeProcInstance(lpProc);\r
2015   }\r
2016 \r
2017   /* Make sure save files land in the right (?) directory */\r
2018   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2019     appData.saveGameFile = strdup(buf);\r
2020   }\r
2021   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2022     appData.savePositionFile = strdup(buf);\r
2023   }\r
2024 \r
2025   /* Finish initialization for fonts and sounds */\r
2026   for (i=0; i<NUM_FONTS; i++) {\r
2027     for (j=0; j<NUM_SIZES; j++) {\r
2028       CreateFontInMF(font[j][i]);\r
2029     }\r
2030   }\r
2031   /* xboard, and older WinBoards, controlled the move sound with the\r
2032      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2033      always turn the option on (so that the backend will call us),\r
2034      then let the user turn the sound off by setting it to silence if\r
2035      desired.  To accommodate old winboard.ini files saved by old\r
2036      versions of WinBoard, we also turn off the sound if the option\r
2037      was initially set to false. */\r
2038   if (!appData.ringBellAfterMoves) {\r
2039     sounds[(int)SoundMove].name = strdup("");\r
2040     appData.ringBellAfterMoves = TRUE;\r
2041   }\r
2042   GetCurrentDirectory(MSG_SIZ, currDir);\r
2043   SetCurrentDirectory(installDir);\r
2044   LoadAllSounds();\r
2045   SetCurrentDirectory(currDir);\r
2046 \r
2047   p = icsTextMenuString;\r
2048   if (p[0] == '@') {\r
2049     FILE* f = fopen(p + 1, "r");\r
2050     if (f == NULL) {\r
2051       DisplayFatalError(p + 1, errno, 2);\r
2052       return;\r
2053     }\r
2054     i = fread(buf, 1, sizeof(buf)-1, f);\r
2055     fclose(f);\r
2056     buf[i] = NULLCHAR;\r
2057     p = buf;\r
2058   }\r
2059   ParseIcsTextMenu(strdup(p));\r
2060 }\r
2061 \r
2062 \r
2063 VOID\r
2064 InitMenuChecks()\r
2065 {\r
2066   HMENU hmenu = GetMenu(hwndMain);\r
2067 \r
2068   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2069                         MF_BYCOMMAND|((appData.icsActive &&\r
2070                                        *appData.icsCommPort != NULLCHAR) ?\r
2071                                       MF_ENABLED : MF_GRAYED));\r
2072   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2073                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2074                                      MF_CHECKED : MF_UNCHECKED));\r
2075 }\r
2076 \r
2077 \r
2078 VOID\r
2079 SaveSettings(char* name)\r
2080 {\r
2081   FILE *f;\r
2082   ArgDescriptor *ad;\r
2083   WINDOWPLACEMENT wp;\r
2084   char dir[MSG_SIZ];\r
2085 \r
2086   if (!hwndMain) return;\r
2087 \r
2088   GetCurrentDirectory(MSG_SIZ, dir);\r
2089   SetCurrentDirectory(installDir);\r
2090   f = fopen(name, "w");\r
2091   SetCurrentDirectory(dir);\r
2092   if (f == NULL) {\r
2093     DisplayError(name, errno);\r
2094     return;\r
2095   }\r
2096   fprintf(f, ";\n");\r
2097   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2098   fprintf(f, ";\n");\r
2099   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2100   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2101   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2102   fprintf(f, ";\n");\r
2103 \r
2104   wp.length = sizeof(WINDOWPLACEMENT);\r
2105   GetWindowPlacement(hwndMain, &wp);\r
2106   boardX = wp.rcNormalPosition.left;\r
2107   boardY = wp.rcNormalPosition.top;\r
2108 \r
2109   if (hwndConsole) {\r
2110     GetWindowPlacement(hwndConsole, &wp);\r
2111     consoleX = wp.rcNormalPosition.left;\r
2112     consoleY = wp.rcNormalPosition.top;\r
2113     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2114     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2115   }\r
2116 \r
2117   if (analysisDialog) {\r
2118     GetWindowPlacement(analysisDialog, &wp);\r
2119     analysisX = wp.rcNormalPosition.left;\r
2120     analysisY = wp.rcNormalPosition.top;\r
2121     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2122     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2123   }\r
2124 \r
2125   if (commentDialog) {\r
2126     GetWindowPlacement(commentDialog, &wp);\r
2127     commentX = wp.rcNormalPosition.left;\r
2128     commentY = wp.rcNormalPosition.top;\r
2129     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2130     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2131   }\r
2132 \r
2133   if (editTagsDialog) {\r
2134     GetWindowPlacement(editTagsDialog, &wp);\r
2135     editTagsX = wp.rcNormalPosition.left;\r
2136     editTagsY = wp.rcNormalPosition.top;\r
2137     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2138     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2139   }\r
2140 \r
2141   if (gameListDialog) {\r
2142     GetWindowPlacement(gameListDialog, &wp);\r
2143     gameListX = wp.rcNormalPosition.left;\r
2144     gameListY = wp.rcNormalPosition.top;\r
2145     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2146     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2147   }\r
2148 \r
2149   /* [AS] Move history */\r
2150   wpMoveHistory.visible = MoveHistoryIsUp();\r
2151   \r
2152   if( moveHistoryDialog ) {\r
2153     GetWindowPlacement(moveHistoryDialog, &wp);\r
2154     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2155     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2156     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2157     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2158   }\r
2159 \r
2160   /* [AS] Eval graph */\r
2161   wpEvalGraph.visible = EvalGraphIsUp();\r
2162 \r
2163   if( evalGraphDialog ) {\r
2164     GetWindowPlacement(evalGraphDialog, &wp);\r
2165     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2166     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2167     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2168     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2169   }\r
2170 \r
2171   /* [AS] Engine output */\r
2172   wpEngineOutput.visible = EngineOutputIsUp();\r
2173 \r
2174   if( engineOutputDialog ) {\r
2175     GetWindowPlacement(engineOutputDialog, &wp);\r
2176     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2177     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2178     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2179     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2180   }\r
2181 \r
2182   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2183     if (!ad->save) continue;\r
2184     switch (ad->argType) {\r
2185     case ArgString:\r
2186       {\r
2187         char *p = *(char **)ad->argLoc;\r
2188         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2189           /* Quote multiline values or \-containing values\r
2190              with { } if possible */\r
2191           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2192         } else {\r
2193           /* Else quote with " " */\r
2194           fprintf(f, "/%s=\"", ad->argName);\r
2195           while (*p) {\r
2196             if (*p == '\n') fprintf(f, "\n");\r
2197             else if (*p == '\r') fprintf(f, "\\r");\r
2198             else if (*p == '\t') fprintf(f, "\\t");\r
2199             else if (*p == '\b') fprintf(f, "\\b");\r
2200             else if (*p == '\f') fprintf(f, "\\f");\r
2201             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2202             else if (*p == '\"') fprintf(f, "\\\"");\r
2203             else if (*p == '\\') fprintf(f, "\\\\");\r
2204             else putc(*p, f);\r
2205             p++;\r
2206           }\r
2207           fprintf(f, "\"\n");\r
2208         }\r
2209       }\r
2210       break;\r
2211     case ArgInt:\r
2212       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2213       break;\r
2214     case ArgFloat:\r
2215       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2216       break;\r
2217     case ArgBoolean:\r
2218       fprintf(f, "/%s=%s\n", ad->argName, \r
2219         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2220       break;\r
2221     case ArgTrue:\r
2222       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2223       break;\r
2224     case ArgFalse:\r
2225       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2226       break;\r
2227     case ArgColor:\r
2228       {\r
2229         COLORREF color = *(COLORREF *)ad->argLoc;\r
2230         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2231           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2232       }\r
2233       break;\r
2234     case ArgAttribs:\r
2235       {\r
2236         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2237         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2238           (ta->effects & CFE_BOLD) ? "b" : "",\r
2239           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2240           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2241           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2242           (ta->effects) ? " " : "",\r
2243           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2244       }\r
2245       break;\r
2246     case ArgFilename:\r
2247       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2248         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2249       } else {\r
2250         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2251       }\r
2252       break;\r
2253     case ArgBoardSize:\r
2254       fprintf(f, "/%s=%s\n", ad->argName,\r
2255               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2256       break;\r
2257     case ArgFont:\r
2258       {\r
2259         int bs;\r
2260         for (bs=0; bs<NUM_SIZES; bs++) {\r
2261           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2262           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2263           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2264             ad->argName, mfp->faceName, mfp->pointSize,\r
2265             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2266             mfp->bold ? "b" : "",\r
2267             mfp->italic ? "i" : "",\r
2268             mfp->underline ? "u" : "",\r
2269             mfp->strikeout ? "s" : "");\r
2270         }\r
2271       }\r
2272       break;\r
2273     case ArgCommSettings:\r
2274       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2275     }\r
2276   }\r
2277   fclose(f);\r
2278 }\r
2279 \r
2280 \r
2281 \r
2282 /*---------------------------------------------------------------------------*\\r
2283  *\r
2284  * GDI board drawing routines\r
2285  *\r
2286 \*---------------------------------------------------------------------------*/\r
2287 \r
2288 /* [AS] Draw square using background texture */\r
2289 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2290 {\r
2291     XFORM   x;\r
2292 \r
2293     if( mode == 0 ) {\r
2294         return; /* Should never happen! */\r
2295     }\r
2296 \r
2297     SetGraphicsMode( dst, GM_ADVANCED );\r
2298 \r
2299     switch( mode ) {\r
2300     case 1:\r
2301         /* Identity */\r
2302         break;\r
2303     case 2:\r
2304         /* X reflection */\r
2305         x.eM11 = -1.0;\r
2306         x.eM12 = 0;\r
2307         x.eM21 = 0;\r
2308         x.eM22 = 1.0;\r
2309         x.eDx = (FLOAT) dw + dx - 1;\r
2310         x.eDy = 0;\r
2311         dx = 0;\r
2312         SetWorldTransform( dst, &x );\r
2313         break;\r
2314     case 3:\r
2315         /* Y reflection */\r
2316         x.eM11 = 1.0;\r
2317         x.eM12 = 0;\r
2318         x.eM21 = 0;\r
2319         x.eM22 = -1.0;\r
2320         x.eDx = 0;\r
2321         x.eDy = (FLOAT) dh + dy - 1;\r
2322         dy = 0;\r
2323         SetWorldTransform( dst, &x );\r
2324         break;\r
2325     case 4:\r
2326         /* X/Y flip */\r
2327         x.eM11 = 0;\r
2328         x.eM12 = 1.0;\r
2329         x.eM21 = 1.0;\r
2330         x.eM22 = 0;\r
2331         x.eDx = (FLOAT) dx;\r
2332         x.eDy = (FLOAT) dy;\r
2333         dx = 0;\r
2334         dy = 0;\r
2335         SetWorldTransform( dst, &x );\r
2336         break;\r
2337     }\r
2338 \r
2339     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2340 \r
2341     x.eM11 = 1.0;\r
2342     x.eM12 = 0;\r
2343     x.eM21 = 0;\r
2344     x.eM22 = 1.0;\r
2345     x.eDx = 0;\r
2346     x.eDy = 0;\r
2347     SetWorldTransform( dst, &x );\r
2348 \r
2349     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2350 }\r
2351 \r
2352 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2353 enum {\r
2354     PM_WP = (int) WhitePawn, \r
2355     PM_WN = (int) WhiteKnight, \r
2356     PM_WB = (int) WhiteBishop, \r
2357     PM_WR = (int) WhiteRook, \r
2358     PM_WQ = (int) WhiteQueen, \r
2359     PM_WF = (int) WhiteFerz, \r
2360     PM_WW = (int) WhiteWazir, \r
2361     PM_WE = (int) WhiteAlfil, \r
2362     PM_WM = (int) WhiteMan, \r
2363     PM_WO = (int) WhiteCannon, \r
2364     PM_WU = (int) WhiteUnicorn, \r
2365     PM_WH = (int) WhiteNightrider, \r
2366     PM_WA = (int) WhiteAngel, \r
2367     PM_WC = (int) WhiteMarshall, \r
2368     PM_WG = (int) WhiteGrasshopper, \r
2369     PM_WK = (int) WhiteKing,\r
2370     PM_BP = (int) BlackPawn, \r
2371     PM_BN = (int) BlackKnight, \r
2372     PM_BB = (int) BlackBishop, \r
2373     PM_BR = (int) BlackRook, \r
2374     PM_BQ = (int) BlackQueen, \r
2375     PM_BF = (int) BlackFerz, \r
2376     PM_BW = (int) BlackWazir, \r
2377     PM_BE = (int) BlackAlfil, \r
2378     PM_BM = (int) BlackMan,\r
2379     PM_BO = (int) BlackCannon, \r
2380     PM_BU = (int) BlackUnicorn, \r
2381     PM_BH = (int) BlackNightrider, \r
2382     PM_BA = (int) BlackAngel, \r
2383     PM_BC = (int) BlackMarshall, \r
2384     PM_BG = (int) BlackGrasshopper, \r
2385     PM_BK = (int) BlackKing\r
2386 };\r
2387 \r
2388 static HFONT hPieceFont = NULL;\r
2389 static HBITMAP hPieceMask[(int) EmptySquare];\r
2390 static HBITMAP hPieceFace[(int) EmptySquare];\r
2391 static int fontBitmapSquareSize = 0;\r
2392 static char pieceToFontChar[(int) EmptySquare] =\r
2393                               { 'p', 'n', 'b', 'r', 'q', \r
2394                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2395                       'k', 'o', 'm', 'v', 't', 'w', \r
2396                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2397                                                               'l' };\r
2398 \r
2399 extern BOOL SetCharTable( char *table, const char * map );\r
2400 /* [HGM] moved to backend.c */\r
2401 \r
2402 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2403 {\r
2404     HBRUSH hbrush;\r
2405     BYTE r1 = GetRValue( color );\r
2406     BYTE g1 = GetGValue( color );\r
2407     BYTE b1 = GetBValue( color );\r
2408     BYTE r2 = r1 / 2;\r
2409     BYTE g2 = g1 / 2;\r
2410     BYTE b2 = b1 / 2;\r
2411     RECT rc;\r
2412 \r
2413     /* Create a uniform background first */\r
2414     hbrush = CreateSolidBrush( color );\r
2415     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2416     FillRect( hdc, &rc, hbrush );\r
2417     DeleteObject( hbrush );\r
2418     \r
2419     if( mode == 1 ) {\r
2420         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2421         int steps = squareSize / 2;\r
2422         int i;\r
2423 \r
2424         for( i=0; i<steps; i++ ) {\r
2425             BYTE r = r1 - (r1-r2) * i / steps;\r
2426             BYTE g = g1 - (g1-g2) * i / steps;\r
2427             BYTE b = b1 - (b1-b2) * i / steps;\r
2428 \r
2429             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2430             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2431             FillRect( hdc, &rc, hbrush );\r
2432             DeleteObject(hbrush);\r
2433         }\r
2434     }\r
2435     else if( mode == 2 ) {\r
2436         /* Diagonal gradient, good more or less for every piece */\r
2437         POINT triangle[3];\r
2438         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2439         HBRUSH hbrush_old;\r
2440         int steps = squareSize;\r
2441         int i;\r
2442 \r
2443         triangle[0].x = squareSize - steps;\r
2444         triangle[0].y = squareSize;\r
2445         triangle[1].x = squareSize;\r
2446         triangle[1].y = squareSize;\r
2447         triangle[2].x = squareSize;\r
2448         triangle[2].y = squareSize - steps;\r
2449 \r
2450         for( i=0; i<steps; i++ ) {\r
2451             BYTE r = r1 - (r1-r2) * i / steps;\r
2452             BYTE g = g1 - (g1-g2) * i / steps;\r
2453             BYTE b = b1 - (b1-b2) * i / steps;\r
2454 \r
2455             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2456             hbrush_old = SelectObject( hdc, hbrush );\r
2457             Polygon( hdc, triangle, 3 );\r
2458             SelectObject( hdc, hbrush_old );\r
2459             DeleteObject(hbrush);\r
2460             triangle[0].x++;\r
2461             triangle[2].y++;\r
2462         }\r
2463 \r
2464         SelectObject( hdc, hpen );\r
2465     }\r
2466 }\r
2467 \r
2468 /*\r
2469     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2470     seems to work ok. The main problem here is to find the "inside" of a chess\r
2471     piece: follow the steps as explained below.\r
2472 */\r
2473 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2474 {\r
2475     HBITMAP hbm;\r
2476     HBITMAP hbm_old;\r
2477     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2478     RECT rc;\r
2479     SIZE sz;\r
2480     POINT pt;\r
2481     int backColor = whitePieceColor; \r
2482     int foreColor = blackPieceColor;\r
2483     int shapeIndex = index < 6 ? index+6 : index;\r
2484     \r
2485     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2486         backColor = appData.fontBackColorWhite;\r
2487         foreColor = appData.fontForeColorWhite;\r
2488     }\r
2489     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2490         backColor = appData.fontBackColorBlack;\r
2491         foreColor = appData.fontForeColorBlack;\r
2492     }\r
2493 \r
2494     /* Mask */\r
2495     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2496 \r
2497     hbm_old = SelectObject( hdc, hbm );\r
2498 \r
2499     rc.left = 0;\r
2500     rc.top = 0;\r
2501     rc.right = squareSize;\r
2502     rc.bottom = squareSize;\r
2503 \r
2504     /* Step 1: background is now black */\r
2505     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2506 \r
2507     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2508 \r
2509     pt.x = (squareSize - sz.cx) / 2;\r
2510     pt.y = (squareSize - sz.cy) / 2;\r
2511 \r
2512     SetBkMode( hdc, TRANSPARENT );\r
2513     SetTextColor( hdc, chroma );\r
2514     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2515     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2516 \r
2517     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2518     /* Step 3: the area outside the piece is filled with white */\r
2519     FloodFill( hdc, 0, 0, chroma );\r
2520     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2521     /* \r
2522         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2523         but if the start point is not inside the piece we're lost!\r
2524         There should be a better way to do this... if we could create a region or path\r
2525         from the fill operation we would be fine for example.\r
2526     */\r
2527     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2528 \r
2529     SetTextColor( hdc, 0 );\r
2530     /* \r
2531         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2532         draw the piece again in black for safety.\r
2533     */\r
2534     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2535 \r
2536     SelectObject( hdc, hbm_old );\r
2537 \r
2538     if( hPieceMask[index] != NULL ) {\r
2539         DeleteObject( hPieceMask[index] );\r
2540     }\r
2541 \r
2542     hPieceMask[index] = hbm;\r
2543 \r
2544     /* Face */\r
2545     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2546 \r
2547     SelectObject( hdc, hbm );\r
2548 \r
2549     {\r
2550         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2551         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2552         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2553 \r
2554         SelectObject( dc1, hPieceMask[index] );\r
2555         SelectObject( dc2, bm2 );\r
2556         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2557         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2558         \r
2559         /* \r
2560             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2561             the piece background and deletes (makes transparent) the rest.\r
2562             Thanks to that mask, we are free to paint the background with the greates\r
2563             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2564             We use this, to make gradients and give the pieces a "roundish" look.\r
2565         */\r
2566         SetPieceBackground( hdc, backColor, 2 );\r
2567         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2568 \r
2569         DeleteDC( dc2 );\r
2570         DeleteDC( dc1 );\r
2571         DeleteObject( bm2 );\r
2572     }\r
2573 \r
2574     SetTextColor( hdc, foreColor );\r
2575     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2576 \r
2577     SelectObject( hdc, hbm_old );\r
2578 \r
2579     if( hPieceFace[index] != NULL ) {\r
2580         DeleteObject( hPieceFace[index] );\r
2581     }\r
2582 \r
2583     hPieceFace[index] = hbm;\r
2584 }\r
2585 \r
2586 static int TranslatePieceToFontPiece( int piece )\r
2587 {\r
2588     switch( piece ) {\r
2589     case BlackPawn:\r
2590         return PM_BP;\r
2591     case BlackKnight:\r
2592         return PM_BN;\r
2593     case BlackBishop:\r
2594         return PM_BB;\r
2595     case BlackRook:\r
2596         return PM_BR;\r
2597     case BlackQueen:\r
2598         return PM_BQ;\r
2599     case BlackKing:\r
2600         return PM_BK;\r
2601     case WhitePawn:\r
2602         return PM_WP;\r
2603     case WhiteKnight:\r
2604         return PM_WN;\r
2605     case WhiteBishop:\r
2606         return PM_WB;\r
2607     case WhiteRook:\r
2608         return PM_WR;\r
2609     case WhiteQueen:\r
2610         return PM_WQ;\r
2611     case WhiteKing:\r
2612         return PM_WK;\r
2613     case BlackAngel:\r
2614         return PM_BA;\r
2615     case BlackMarshall:\r
2616         return PM_BC;\r
2617     case BlackFerz:\r
2618         return PM_BF;\r
2619     case BlackNightrider:\r
2620         return PM_BH;\r
2621     case BlackAlfil:\r
2622         return PM_BE;\r
2623     case BlackWazir:\r
2624         return PM_BW;\r
2625     case BlackUnicorn:\r
2626         return PM_BU;\r
2627     case BlackCannon:\r
2628         return PM_BO;\r
2629     case BlackGrasshopper:\r
2630         return PM_BG;\r
2631     case BlackMan:\r
2632         return PM_BM;\r
2633     case WhiteAngel:\r
2634         return PM_WA;\r
2635     case WhiteMarshall:\r
2636         return PM_WC;\r
2637     case WhiteFerz:\r
2638         return PM_WF;\r
2639     case WhiteNightrider:\r
2640         return PM_WH;\r
2641     case WhiteAlfil:\r
2642         return PM_WE;\r
2643     case WhiteWazir:\r
2644         return PM_WW;\r
2645     case WhiteUnicorn:\r
2646         return PM_WU;\r
2647     case WhiteCannon:\r
2648         return PM_WO;\r
2649     case WhiteGrasshopper:\r
2650         return PM_WG;\r
2651     case WhiteMan:\r
2652         return PM_WM;\r
2653     }\r
2654 \r
2655     return 0;\r
2656 }\r
2657 \r
2658 void CreatePiecesFromFont()\r
2659 {\r
2660     LOGFONT lf;\r
2661     HDC hdc_window = NULL;\r
2662     HDC hdc = NULL;\r
2663     HFONT hfont_old;\r
2664     int fontHeight;\r
2665     int i;\r
2666 \r
2667     if( fontBitmapSquareSize < 0 ) {\r
2668         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2669         return;\r
2670     }\r
2671 \r
2672     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2673         fontBitmapSquareSize = -1;\r
2674         return;\r
2675     }\r
2676 \r
2677     if( fontBitmapSquareSize != squareSize ) {\r
2678         hdc_window = GetDC( hwndMain );\r
2679         hdc = CreateCompatibleDC( hdc_window );\r
2680 \r
2681         if( hPieceFont != NULL ) {\r
2682             DeleteObject( hPieceFont );\r
2683         }\r
2684         else {\r
2685             for( i=0; i<12; i++ ) {\r
2686                 hPieceMask[i] = NULL;\r
2687                 hPieceFace[i] = NULL;\r
2688             }\r
2689         }\r
2690 \r
2691         fontHeight = 75;\r
2692 \r
2693         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2694             fontHeight = appData.fontPieceSize;\r
2695         }\r
2696 \r
2697         fontHeight = (fontHeight * squareSize) / 100;\r
2698 \r
2699         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2700         lf.lfWidth = 0;\r
2701         lf.lfEscapement = 0;\r
2702         lf.lfOrientation = 0;\r
2703         lf.lfWeight = FW_NORMAL;\r
2704         lf.lfItalic = 0;\r
2705         lf.lfUnderline = 0;\r
2706         lf.lfStrikeOut = 0;\r
2707         lf.lfCharSet = DEFAULT_CHARSET;\r
2708         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2709         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2710         lf.lfQuality = PROOF_QUALITY;\r
2711         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2712         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2713         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2714 \r
2715         hPieceFont = CreateFontIndirect( &lf );\r
2716 \r
2717         if( hPieceFont == NULL ) {\r
2718             fontBitmapSquareSize = -2;\r
2719         }\r
2720         else {\r
2721             /* Setup font-to-piece character table */\r
2722             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2723                 /* No (or wrong) global settings, try to detect the font */\r
2724                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2725                     /* Alpha */\r
2726                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2727                 }\r
2728                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2729                     /* DiagramTT* family */\r
2730                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2731                 }\r
2732                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2733                     /* Fairy symbols */\r
2734                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2735                 }\r
2736                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2737                     /* Good Companion (Some characters get warped as literal :-( */\r
2738                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2739                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2740                     SetCharTable(pieceToFontChar, s);\r
2741                 }\r
2742                 else {\r
2743                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2744                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2745                 }\r
2746             }\r
2747 \r
2748             /* Create bitmaps */\r
2749             hfont_old = SelectObject( hdc, hPieceFont );\r
2750 \r
2751             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2752             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2753             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2754             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2755             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2756             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2757             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2758             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2759             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2760             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2761             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2762             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2763 #ifdef FAIRY\r
2764             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2765             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2766             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2767             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2768             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2769             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2770             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2771             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2772             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2773             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2774             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2775             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2776             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2777             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2778             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2779             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2780             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2781             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2782             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2783             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2784 #endif\r
2785 \r
2786             SelectObject( hdc, hfont_old );\r
2787 \r
2788             fontBitmapSquareSize = squareSize;\r
2789         }\r
2790     }\r
2791 \r
2792     if( hdc != NULL ) {\r
2793         DeleteDC( hdc );\r
2794     }\r
2795 \r
2796     if( hdc_window != NULL ) {\r
2797         ReleaseDC( hwndMain, hdc_window );\r
2798     }\r
2799 }\r
2800 \r
2801 HBITMAP\r
2802 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2803 {\r
2804   char name[128];\r
2805 \r
2806   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2807   if (gameInfo.event &&\r
2808       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2809       strcmp(name, "k80s") == 0) {\r
2810     strcpy(name, "tim");\r
2811   }\r
2812   return LoadBitmap(hinst, name);\r
2813 }\r
2814 \r
2815 \r
2816 /* Insert a color into the program's logical palette\r
2817    structure.  This code assumes the given color is\r
2818    the result of the RGB or PALETTERGB macro, and it\r
2819    knows how those macros work (which is documented).\r
2820 */\r
2821 VOID\r
2822 InsertInPalette(COLORREF color)\r
2823 {\r
2824   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2825 \r
2826   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2827     DisplayFatalError("Too many colors", 0, 1);\r
2828     pLogPal->palNumEntries--;\r
2829     return;\r
2830   }\r
2831 \r
2832   pe->peFlags = (char) 0;\r
2833   pe->peRed = (char) (0xFF & color);\r
2834   pe->peGreen = (char) (0xFF & (color >> 8));\r
2835   pe->peBlue = (char) (0xFF & (color >> 16));\r
2836   return;\r
2837 }\r
2838 \r
2839 \r
2840 VOID\r
2841 InitDrawingColors()\r
2842 {\r
2843   if (pLogPal == NULL) {\r
2844     /* Allocate enough memory for a logical palette with\r
2845      * PALETTESIZE entries and set the size and version fields\r
2846      * of the logical palette structure.\r
2847      */\r
2848     pLogPal = (NPLOGPALETTE)\r
2849       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2850                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2851     pLogPal->palVersion    = 0x300;\r
2852   }\r
2853   pLogPal->palNumEntries = 0;\r
2854 \r
2855   InsertInPalette(lightSquareColor);\r
2856   InsertInPalette(darkSquareColor);\r
2857   InsertInPalette(whitePieceColor);\r
2858   InsertInPalette(blackPieceColor);\r
2859   InsertInPalette(highlightSquareColor);\r
2860   InsertInPalette(premoveHighlightColor);\r
2861 \r
2862   /*  create a logical color palette according the information\r
2863    *  in the LOGPALETTE structure.\r
2864    */\r
2865   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2866 \r
2867   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2868   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2869   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2870   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2871   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2872   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2873 \r
2874   /* [AS] Force rendering of the font-based pieces */\r
2875   if( fontBitmapSquareSize > 0 ) {\r
2876     fontBitmapSquareSize = 0;\r
2877   }\r
2878 }\r
2879 \r
2880 \r
2881 int\r
2882 BoardWidth(int boardSize, int n)\r
2883 { /* [HGM] argument n added to allow different width and height */\r
2884   int lineGap = sizeInfo[boardSize].lineGap;\r
2885 \r
2886   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2887       lineGap = appData.overrideLineGap;\r
2888   }\r
2889 \r
2890   return (n + 1) * lineGap +\r
2891           n * sizeInfo[boardSize].squareSize;\r
2892 }\r
2893 \r
2894 /* Respond to board resize by dragging edge */\r
2895 VOID\r
2896 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2897 {\r
2898   BoardSize newSize = NUM_SIZES - 1;\r
2899   static int recurse = 0;\r
2900   if (IsIconic(hwndMain)) return;\r
2901   if (recurse > 0) return;\r
2902   recurse++;\r
2903   while (newSize > 0 &&\r
2904          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2905           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2906     newSize--;\r
2907   } \r
2908   boardSize = newSize;\r
2909   InitDrawingSizes(boardSize, flags);\r
2910   recurse--;\r
2911 }\r
2912 \r
2913 \r
2914 \r
2915 VOID\r
2916 InitDrawingSizes(BoardSize boardSize, int flags)\r
2917 {\r
2918   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2919   ChessSquare piece;\r
2920   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2921   HDC hdc;\r
2922   SIZE clockSize, messageSize;\r
2923   HFONT oldFont;\r
2924   char buf[MSG_SIZ];\r
2925   char *str;\r
2926   HMENU hmenu = GetMenu(hwndMain);\r
2927   RECT crect, wrect;\r
2928   int offby;\r
2929   LOGBRUSH logbrush;\r
2930 \r
2931   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
2932   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2933 \r
2934   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2935   smallLayout = sizeInfo[boardSize].smallLayout;\r
2936   squareSize = sizeInfo[boardSize].squareSize;\r
2937   lineGap = sizeInfo[boardSize].lineGap;\r
2938   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2939 \r
2940   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2941       lineGap = appData.overrideLineGap;\r
2942   }\r
2943 \r
2944   if (tinyLayout != oldTinyLayout) {\r
2945     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2946     if (tinyLayout) {\r
2947       style &= ~WS_SYSMENU;\r
2948       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2949                  "&Minimize\tCtrl+F4");\r
2950     } else {\r
2951       style |= WS_SYSMENU;\r
2952       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2953     }\r
2954     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2955 \r
2956     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2957       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2958         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2959     }\r
2960     DrawMenuBar(hwndMain);\r
2961   }\r
2962 \r
2963   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2964   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2965 \r
2966   /* Get text area sizes */\r
2967   hdc = GetDC(hwndMain);\r
2968   if (appData.clockMode) {\r
2969     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2970   } else {\r
2971     sprintf(buf, "White");\r
2972   }\r
2973   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2974   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2975   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2976   str = "We only care about the height here";\r
2977   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2978   SelectObject(hdc, oldFont);\r
2979   ReleaseDC(hwndMain, hdc);\r
2980 \r
2981   /* Compute where everything goes */\r
2982   whiteRect.left = OUTER_MARGIN;\r
2983   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2984   whiteRect.top = OUTER_MARGIN;\r
2985   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2986 \r
2987   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2988   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2989   blackRect.top = whiteRect.top;\r
2990   blackRect.bottom = whiteRect.bottom;\r
2991 \r
2992   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2993   if (appData.showButtonBar) {\r
2994     messageRect.right = blackRect.right\r
2995       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2996   } else {\r
2997     messageRect.right = blackRect.right;\r
2998   }\r
2999   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3000   messageRect.bottom = messageRect.top + messageSize.cy;\r
3001 \r
3002   boardRect.left = whiteRect.left;\r
3003   boardRect.right = boardRect.left + boardWidth;\r
3004   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3005   boardRect.bottom = boardRect.top + boardHeight;\r
3006 \r
3007   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3008   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3009   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3010   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3011     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3012   GetWindowRect(hwndMain, &wrect);\r
3013   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3014                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3015   /* compensate if menu bar wrapped */\r
3016   GetClientRect(hwndMain, &crect);\r
3017   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3018   winHeight += offby;\r
3019   switch (flags) {\r
3020   case WMSZ_TOPLEFT:\r
3021     SetWindowPos(hwndMain, NULL, \r
3022                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3023                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3024     break;\r
3025 \r
3026   case WMSZ_TOPRIGHT:\r
3027   case WMSZ_TOP:\r
3028     SetWindowPos(hwndMain, NULL, \r
3029                  wrect.left, wrect.bottom - winHeight, \r
3030                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3031     break;\r
3032 \r
3033   case WMSZ_BOTTOMLEFT:\r
3034   case WMSZ_LEFT:\r
3035     SetWindowPos(hwndMain, NULL, \r
3036                  wrect.right - winWidth, wrect.top, \r
3037                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3038     break;\r
3039 \r
3040   case WMSZ_BOTTOMRIGHT:\r
3041   case WMSZ_BOTTOM:\r
3042   case WMSZ_RIGHT:\r
3043   default:\r
3044     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3045                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3046     break;\r
3047   }\r
3048 \r
3049   hwndPause = NULL;\r
3050   for (i = 0; i < N_BUTTONS; i++) {\r
3051     if (buttonDesc[i].hwnd != NULL) {\r
3052       DestroyWindow(buttonDesc[i].hwnd);\r
3053       buttonDesc[i].hwnd = NULL;\r
3054     }\r
3055     if (appData.showButtonBar) {\r
3056       buttonDesc[i].hwnd =\r
3057         CreateWindow("BUTTON", buttonDesc[i].label,\r
3058                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3059                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3060                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3061                      (HMENU) buttonDesc[i].id,\r
3062                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3063       if (tinyLayout) {\r
3064         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3065                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3066                     MAKELPARAM(FALSE, 0));\r
3067       }\r
3068       if (buttonDesc[i].id == IDM_Pause)\r
3069         hwndPause = buttonDesc[i].hwnd;\r
3070       buttonDesc[i].wndproc = (WNDPROC)\r
3071         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3072     }\r
3073   }\r
3074   if (gridPen != NULL) DeleteObject(gridPen);\r
3075   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3076   if (premovePen != NULL) DeleteObject(premovePen);\r
3077   if (lineGap != 0) {\r
3078     logbrush.lbStyle = BS_SOLID;\r
3079     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3080     gridPen =\r
3081       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3082                    lineGap, &logbrush, 0, NULL);\r
3083     logbrush.lbColor = highlightSquareColor;\r
3084     highlightPen =\r
3085       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3086                    lineGap, &logbrush, 0, NULL);\r
3087 \r
3088     logbrush.lbColor = premoveHighlightColor; \r
3089     premovePen =\r
3090       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3091                    lineGap, &logbrush, 0, NULL);\r
3092 \r
3093     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3094     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3095       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3096       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3097         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3098       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3099         BOARD_WIDTH * (squareSize + lineGap);\r
3100         lineGap / 2 + (i * (squareSize + lineGap));\r
3101       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3102     }\r
3103     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3104       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3105       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3106         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3107         lineGap / 2 + (i * (squareSize + lineGap));\r
3108       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3109         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3110       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3111     }\r
3112   }\r
3113 \r
3114   /* [HGM] Licensing requirement */\r
3115 #ifdef GOTHIC\r
3116   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3117 #endif\r
3118 #ifdef FALCON\r
3119   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3120 #endif\r
3121   GothicPopUp( "", VariantNormal);\r
3122 \r
3123 \r
3124 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3125   oldBoardSize = boardSize;\r
3126   oldTinyLayout = tinyLayout;\r
3127 \r
3128   /* Load piece bitmaps for this board size */\r
3129   for (i=0; i<=2; i++) {\r
3130     for (piece = WhitePawn;\r
3131          (int) piece < (int) BlackPawn;\r
3132          piece = (ChessSquare) ((int) piece + 1)) {\r
3133       if (pieceBitmap[i][piece] != NULL)\r
3134         DeleteObject(pieceBitmap[i][piece]);\r
3135     }\r
3136   }\r
3137 \r
3138   // Orthodox Chess pieces\r
3139   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3140   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3141   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3142   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3143   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3144   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3145   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3146   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3147   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3148   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3149   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3150   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3151   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3152   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3153   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3154   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3155     // in Shogi, Hijack the unused Queen for Lance\r
3156     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3157     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3158     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3159   } else {\r
3160     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3161     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3162     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3163   }\r
3164 \r
3165   if(squareSize <= 72 && squareSize >= 33) { \r
3166     /* A & C are available in most sizes now */\r
3167     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3168       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3169       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3170       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3171       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3172       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3173       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3174       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3175       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3176       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3177       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3178       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3179       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3180     } else { // Smirf-like\r
3181       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3182       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3183       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3184     }\r
3185     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3186       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3187       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3188       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3189     } else { // WinBoard standard\r
3190       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3191       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3192       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3193     }\r
3194   }\r
3195 \r
3196 \r
3197   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3198     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3199     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3200     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3201     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3202     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3203     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3204     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3205     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3206     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3207     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3208     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3209     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3210     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3211     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3212     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3213     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3214     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3215     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3216     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3217     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3218     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3219     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3220     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3221     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3222     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3223     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3224     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3225     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3226     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3227     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3228 \r
3229     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3230       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3231       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3232       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3233       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3234       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3235       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3236       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3237       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3238       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3239       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3240       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3241       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3242     } else {\r
3243       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3244       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3245       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3246       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3247       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3248       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3249       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3250       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3251       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3252       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3253       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3254       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3255     }\r
3256 \r
3257   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3258     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3259     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3260     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3261     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3262     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3263     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3264     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3265     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3266     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3267     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3268     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3269     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3270     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3271     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3272   }\r
3273 \r
3274 \r
3275   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3276   /* special Shogi support in this size */\r
3277   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3278       for (piece = WhitePawn;\r
3279            (int) piece < (int) BlackPawn;\r
3280            piece = (ChessSquare) ((int) piece + 1)) {\r
3281         if (pieceBitmap[i][piece] != NULL)\r
3282           DeleteObject(pieceBitmap[i][piece]);\r
3283       }\r
3284     }\r
3285   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3286   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3287   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3288   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3289   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3290   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3291   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3292   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3293   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3294   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3295   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3296   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3297   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3298   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3299   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3300   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3301   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3302   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3303   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3304   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3305   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3306   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3307   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3308   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3309   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3310   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3311   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3312   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3313   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3314   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3315   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3316   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3317   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3318   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3319   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3320   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3321   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3322   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3323   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3324   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3325   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3326   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3327   minorSize = 0;\r
3328   }\r
3329 }\r
3330 \r
3331 HBITMAP\r
3332 PieceBitmap(ChessSquare p, int kind)\r
3333 {\r
3334   if ((int) p >= (int) BlackPawn)\r
3335     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3336 \r
3337   return pieceBitmap[kind][(int) p];\r
3338 }\r
3339 \r
3340 /***************************************************************/\r
3341 \r
3342 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3343 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3344 /*\r
3345 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3346 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3347 */\r
3348 \r
3349 VOID\r
3350 SquareToPos(int row, int column, int * x, int * y)\r
3351 {\r
3352   if (flipView) {\r
3353     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3354     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3355   } else {\r
3356     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3357     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3358   }\r
3359 }\r
3360 \r
3361 VOID\r
3362 DrawCoordsOnDC(HDC hdc)\r
3363 {\r
3364   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
3365   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
3366   char str[2] = { NULLCHAR, NULLCHAR };\r
3367   int oldMode, oldAlign, x, y, start, i;\r
3368   HFONT oldFont;\r
3369   HBRUSH oldBrush;\r
3370 \r
3371   if (!appData.showCoords)\r
3372     return;\r
3373 \r
3374   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3375 \r
3376   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3377   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3378   oldAlign = GetTextAlign(hdc);\r
3379   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3380 \r
3381   y = boardRect.top + lineGap;\r
3382   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3383 \r
3384   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3385   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3386     str[0] = files[start + i];\r
3387     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3388     y += squareSize + lineGap;\r
3389   }\r
3390 \r
3391   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3392 \r
3393   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3394   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3395     str[0] = ranks[start + i];\r
3396     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3397     x += squareSize + lineGap;\r
3398   }    \r
3399 \r
3400   SelectObject(hdc, oldBrush);\r
3401   SetBkMode(hdc, oldMode);\r
3402   SetTextAlign(hdc, oldAlign);\r
3403   SelectObject(hdc, oldFont);\r
3404 }\r
3405 \r
3406 VOID\r
3407 DrawGridOnDC(HDC hdc)\r
3408 {\r
3409   HPEN oldPen;\r
3410  \r
3411   if (lineGap != 0) {\r
3412     oldPen = SelectObject(hdc, gridPen);\r
3413     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3414     SelectObject(hdc, oldPen);\r
3415   }\r
3416 }\r
3417 \r
3418 #define HIGHLIGHT_PEN 0\r
3419 #define PREMOVE_PEN   1\r
3420 \r
3421 VOID\r
3422 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3423 {\r
3424   int x1, y1;\r
3425   HPEN oldPen, hPen;\r
3426   if (lineGap == 0) return;\r
3427   if (flipView) {\r
3428     x1 = boardRect.left +\r
3429       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3430     y1 = boardRect.top +\r
3431       lineGap/2 + y * (squareSize + lineGap);\r
3432   } else {\r
3433     x1 = boardRect.left +\r
3434       lineGap/2 + x * (squareSize + lineGap);\r
3435     y1 = boardRect.top +\r
3436       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3437   }\r
3438   hPen = pen ? premovePen : highlightPen;\r
3439   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3440   MoveToEx(hdc, x1, y1, NULL);\r
3441   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3442   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3443   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3444   LineTo(hdc, x1, y1);\r
3445   SelectObject(hdc, oldPen);\r
3446 }\r
3447 \r
3448 VOID\r
3449 DrawHighlightsOnDC(HDC hdc)\r
3450 {\r
3451   int i;\r
3452   for (i=0; i<2; i++) {\r
3453     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3454       DrawHighlightOnDC(hdc, TRUE,\r
3455                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3456                         HIGHLIGHT_PEN);\r
3457   }\r
3458   for (i=0; i<2; i++) {\r
3459     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3460         premoveHighlightInfo.sq[i].y >= 0) {\r
3461         DrawHighlightOnDC(hdc, TRUE,\r
3462                           premoveHighlightInfo.sq[i].x, \r
3463                           premoveHighlightInfo.sq[i].y,\r
3464                           PREMOVE_PEN);\r
3465     }\r
3466   }\r
3467 }\r
3468 \r
3469 /* Note: sqcolor is used only in monoMode */\r
3470 /* Note that this code is largely duplicated in woptions.c,\r
3471    function DrawSampleSquare, so that needs to be updated too */\r
3472 VOID\r
3473 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3474 {\r
3475   HBITMAP oldBitmap;\r
3476   HBRUSH oldBrush;\r
3477   int tmpSize;\r
3478 \r
3479   if (appData.blindfold) return;\r
3480 \r
3481   /* [AS] Use font-based pieces if needed */\r
3482   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3483     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3484     CreatePiecesFromFont();\r
3485 \r
3486     if( fontBitmapSquareSize == squareSize ) {\r
3487         int index = TranslatePieceToFontPiece( piece );\r
3488 \r
3489         SelectObject( tmphdc, hPieceMask[ index ] );\r
3490 \r
3491         BitBlt( hdc,\r
3492             x, y,\r
3493             squareSize, squareSize,\r
3494             tmphdc,\r
3495             0, 0,\r
3496             SRCAND );\r
3497 \r
3498         SelectObject( tmphdc, hPieceFace[ index ] );\r
3499 \r
3500         BitBlt( hdc,\r
3501             x, y,\r
3502             squareSize, squareSize,\r
3503             tmphdc,\r
3504             0, 0,\r
3505             SRCPAINT );\r
3506 \r
3507         return;\r
3508     }\r
3509   }\r
3510 \r
3511   if (appData.monoMode) {\r
3512     SelectObject(tmphdc, PieceBitmap(piece, \r
3513       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3514     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3515            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3516   } else {\r
3517     tmpSize = squareSize;\r
3518     if(minorSize &&\r
3519         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3520          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3521       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3522       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3523       x += (squareSize - minorSize)>>1;\r
3524       y += squareSize - minorSize - 2;\r
3525       tmpSize = minorSize;\r
3526     }\r
3527     if (color || appData.allWhite ) {\r
3528       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3529       if( color )\r
3530               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3531       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3532       if(appData.upsideDown && color==flipView)\r
3533         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3534       else\r
3535         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3536 #if 0\r
3537       /* Use black piece color for outline of white pieces */\r
3538       /* Not sure this looks really good (though xboard does it).\r
3539          Maybe better to have another selectable color, default black */\r
3540       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3541       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3542       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3543 #else\r
3544       /* Use black for outline of white pieces */\r
3545       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3546       if(appData.upsideDown && color==flipView)\r
3547         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3548       else\r
3549         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3550 #endif\r
3551     } else {\r
3552 #if 0\r
3553       /* Use white piece color for details of black pieces */\r
3554       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3555          WHITE_PIECE ones aren't always the right shape. */\r
3556       /* Not sure this looks really good (though xboard does it).\r
3557          Maybe better to have another selectable color, default medium gray? */\r
3558       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3559       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3560       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3561       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3562       SelectObject(hdc, blackPieceBrush);\r
3563       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3564 #else\r
3565       /* Use square color for details of black pieces */\r
3566       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3567       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3568       if(appData.upsideDown && !flipView)\r
3569         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3570       else\r
3571         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3572 #endif\r
3573     }\r
3574     SelectObject(hdc, oldBrush);\r
3575     SelectObject(tmphdc, oldBitmap);\r
3576   }\r
3577 }\r
3578 \r
3579 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3580 int GetBackTextureMode( int algo )\r
3581 {\r
3582     int result = BACK_TEXTURE_MODE_DISABLED;\r
3583 \r
3584     switch( algo ) \r
3585     {\r
3586         case BACK_TEXTURE_MODE_PLAIN:\r
3587             result = 1; /* Always use identity map */\r
3588             break;\r
3589         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3590             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3591             break;\r
3592     }\r
3593 \r
3594     return result;\r
3595 }\r
3596 \r
3597 /* \r
3598     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3599     to handle redraws cleanly (as random numbers would always be different).\r
3600 */\r
3601 VOID RebuildTextureSquareInfo()\r
3602 {\r
3603     BITMAP bi;\r
3604     int lite_w = 0;\r
3605     int lite_h = 0;\r
3606     int dark_w = 0;\r
3607     int dark_h = 0;\r
3608     int row;\r
3609     int col;\r
3610 \r
3611     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3612 \r
3613     if( liteBackTexture != NULL ) {\r
3614         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3615             lite_w = bi.bmWidth;\r
3616             lite_h = bi.bmHeight;\r
3617         }\r
3618     }\r
3619 \r
3620     if( darkBackTexture != NULL ) {\r
3621         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3622             dark_w = bi.bmWidth;\r
3623             dark_h = bi.bmHeight;\r
3624         }\r
3625     }\r
3626 \r
3627     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3628         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3629             if( (col + row) & 1 ) {\r
3630                 /* Lite square */\r
3631                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3632                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3633                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3634                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3635                 }\r
3636             }\r
3637             else {\r
3638                 /* Dark square */\r
3639                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3640                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3641                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3642                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3643                 }\r
3644             }\r
3645         }\r
3646     }\r
3647 }\r
3648 \r
3649 /* [AS] Arrow highlighting support */\r
3650 \r
3651 static int A_WIDTH = 5; /* Width of arrow body */\r
3652 \r
3653 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3654 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3655 \r
3656 static double Sqr( double x )\r
3657 {\r
3658     return x*x;\r
3659 }\r
3660 \r
3661 static int Round( double x )\r
3662 {\r
3663     return (int) (x + 0.5);\r
3664 }\r
3665 \r
3666 /* Draw an arrow between two points using current settings */\r
3667 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3668 {\r
3669     POINT arrow[7];\r
3670     double dx, dy, j, k, x, y;\r
3671 \r
3672     if( d_x == s_x ) {\r
3673         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3674 \r
3675         arrow[0].x = s_x + A_WIDTH;\r
3676         arrow[0].y = s_y;\r
3677 \r
3678         arrow[1].x = s_x + A_WIDTH;\r
3679         arrow[1].y = d_y - h;\r
3680 \r
3681         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3682         arrow[2].y = d_y - h;\r
3683 \r
3684         arrow[3].x = d_x;\r
3685         arrow[3].y = d_y;\r
3686 \r
3687         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3688         arrow[4].y = d_y - h;\r
3689 \r
3690         arrow[5].x = s_x - A_WIDTH;\r
3691         arrow[5].y = d_y - h;\r
3692 \r
3693         arrow[6].x = s_x - A_WIDTH;\r
3694         arrow[6].y = s_y;\r
3695     }\r
3696     else if( d_y == s_y ) {\r
3697         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3698 \r
3699         arrow[0].x = s_x;\r
3700         arrow[0].y = s_y + A_WIDTH;\r
3701 \r
3702         arrow[1].x = d_x - w;\r
3703         arrow[1].y = s_y + A_WIDTH;\r
3704 \r
3705         arrow[2].x = d_x - w;\r
3706         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3707 \r
3708         arrow[3].x = d_x;\r
3709         arrow[3].y = d_y;\r
3710 \r
3711         arrow[4].x = d_x - w;\r
3712         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3713 \r
3714         arrow[5].x = d_x - w;\r
3715         arrow[5].y = s_y - A_WIDTH;\r
3716 \r
3717         arrow[6].x = s_x;\r
3718         arrow[6].y = s_y - A_WIDTH;\r
3719     }\r
3720     else {\r
3721         /* [AS] Needed a lot of paper for this! :-) */\r
3722         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3723         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3724   \r
3725         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3726 \r
3727         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3728 \r
3729         x = s_x;\r
3730         y = s_y;\r
3731 \r
3732         arrow[0].x = Round(x - j);\r
3733         arrow[0].y = Round(y + j*dx);\r
3734 \r
3735         arrow[1].x = Round(x + j);\r
3736         arrow[1].y = Round(y - j*dx);\r
3737 \r
3738         if( d_x > s_x ) {\r
3739             x = (double) d_x - k;\r
3740             y = (double) d_y - k*dy;\r
3741         }\r
3742         else {\r
3743             x = (double) d_x + k;\r
3744             y = (double) d_y + k*dy;\r
3745         }\r
3746 \r
3747         arrow[2].x = Round(x + j);\r
3748         arrow[2].y = Round(y - j*dx);\r
3749 \r
3750         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3751         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3752 \r
3753         arrow[4].x = d_x;\r
3754         arrow[4].y = d_y;\r
3755 \r
3756         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3757         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3758 \r
3759         arrow[6].x = Round(x - j);\r
3760         arrow[6].y = Round(y + j*dx);\r
3761     }\r
3762 \r
3763     Polygon( hdc, arrow, 7 );\r
3764 }\r
3765 \r
3766 /* [AS] Draw an arrow between two squares */\r
3767 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3768 {\r
3769     int s_x, s_y, d_x, d_y;\r
3770     HPEN hpen;\r
3771     HPEN holdpen;\r
3772     HBRUSH hbrush;\r
3773     HBRUSH holdbrush;\r
3774     LOGBRUSH stLB;\r
3775 \r
3776     if( s_col == d_col && s_row == d_row ) {\r
3777         return;\r
3778     }\r
3779 \r
3780     /* Get source and destination points */\r
3781     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3782     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3783 \r
3784     if( d_y > s_y ) {\r
3785         d_y += squareSize / 4;\r
3786     }\r
3787     else if( d_y < s_y ) {\r
3788         d_y += 3 * squareSize / 4;\r
3789     }\r
3790     else {\r
3791         d_y += squareSize / 2;\r
3792     }\r
3793 \r
3794     if( d_x > s_x ) {\r
3795         d_x += squareSize / 4;\r
3796     }\r
3797     else if( d_x < s_x ) {\r
3798         d_x += 3 * squareSize / 4;\r
3799     }\r
3800     else {\r
3801         d_x += squareSize / 2;\r
3802     }\r
3803 \r
3804     s_x += squareSize / 2;\r
3805     s_y += squareSize / 2;\r
3806 \r
3807     /* Adjust width */\r
3808     A_WIDTH = squareSize / 14;\r
3809 \r
3810     /* Draw */\r
3811     stLB.lbStyle = BS_SOLID;\r
3812     stLB.lbColor = appData.highlightArrowColor;\r
3813     stLB.lbHatch = 0;\r
3814 \r
3815     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3816     holdpen = SelectObject( hdc, hpen );\r
3817     hbrush = CreateBrushIndirect( &stLB );\r
3818     holdbrush = SelectObject( hdc, hbrush );\r
3819 \r
3820     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3821 \r
3822     SelectObject( hdc, holdpen );\r
3823     SelectObject( hdc, holdbrush );\r
3824     DeleteObject( hpen );\r
3825     DeleteObject( hbrush );\r
3826 }\r
3827 \r
3828 BOOL HasHighlightInfo()\r
3829 {\r
3830     BOOL result = FALSE;\r
3831 \r
3832     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3833         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3834     {\r
3835         result = TRUE;\r
3836     }\r
3837 \r
3838     return result;\r
3839 }\r
3840 \r
3841 BOOL IsDrawArrowEnabled()\r
3842 {\r
3843     BOOL result = FALSE;\r
3844 \r
3845     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3846         result = TRUE;\r
3847     }\r
3848 \r
3849     return result;\r
3850 }\r
3851 \r
3852 VOID DrawArrowHighlight( HDC hdc )\r
3853 {\r
3854     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3855         DrawArrowBetweenSquares( hdc,\r
3856             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3857             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3858     }\r
3859 }\r
3860 \r
3861 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3862 {\r
3863     HRGN result = NULL;\r
3864 \r
3865     if( HasHighlightInfo() ) {\r
3866         int x1, y1, x2, y2;\r
3867         int sx, sy, dx, dy;\r
3868 \r
3869         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3870         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3871 \r
3872         sx = MIN( x1, x2 );\r
3873         sy = MIN( y1, y2 );\r
3874         dx = MAX( x1, x2 ) + squareSize;\r
3875         dy = MAX( y1, y2 ) + squareSize;\r
3876 \r
3877         result = CreateRectRgn( sx, sy, dx, dy );\r
3878     }\r
3879 \r
3880     return result;\r
3881 }\r
3882 \r
3883 /*\r
3884     Warning: this function modifies the behavior of several other functions. \r
3885     \r
3886     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3887     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3888     repaint is scattered all over the place, which is not good for features such as\r
3889     "arrow highlighting" that require a full repaint of the board.\r
3890 \r
3891     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3892     user interaction, when speed is not so important) but especially to avoid errors\r
3893     in the displayed graphics.\r
3894 \r
3895     In such patched places, I always try refer to this function so there is a single\r
3896     place to maintain knowledge.\r
3897     \r
3898     To restore the original behavior, just return FALSE unconditionally.\r
3899 */\r
3900 BOOL IsFullRepaintPreferrable()\r
3901 {\r
3902     BOOL result = FALSE;\r
3903 \r
3904     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3905         /* Arrow may appear on the board */\r
3906         result = TRUE;\r
3907     }\r
3908 \r
3909     return result;\r
3910 }\r
3911 \r
3912 /* \r
3913     This function is called by DrawPosition to know whether a full repaint must\r
3914     be forced or not.\r
3915 \r
3916     Only DrawPosition may directly call this function, which makes use of \r
3917     some state information. Other function should call DrawPosition specifying \r
3918     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3919 */\r
3920 BOOL DrawPositionNeedsFullRepaint()\r
3921 {\r
3922     BOOL result = FALSE;\r
3923 \r
3924     /* \r
3925         Probably a slightly better policy would be to trigger a full repaint\r
3926         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3927         but animation is fast enough that it's difficult to notice.\r
3928     */\r
3929     if( animInfo.piece == EmptySquare ) {\r
3930         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3931             result = TRUE;\r
3932         }\r
3933     }\r
3934 \r
3935     return result;\r
3936 }\r
3937 \r
3938 VOID\r
3939 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3940 {\r
3941   int row, column, x, y, square_color, piece_color;\r
3942   ChessSquare piece;\r
3943   HBRUSH oldBrush;\r
3944   HDC texture_hdc = NULL;\r
3945 \r
3946   /* [AS] Initialize background textures if needed */\r
3947   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3948       if( backTextureSquareSize != squareSize ) {\r
3949           backTextureSquareSize = squareSize;\r
3950           RebuildTextureSquareInfo();\r
3951       }\r
3952 \r
3953       texture_hdc = CreateCompatibleDC( hdc );\r
3954   }\r
3955 \r
3956   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3957     for (column = 0; column < BOARD_WIDTH; column++) {\r
3958   \r
3959       SquareToPos(row, column, &x, &y);\r
3960 \r
3961       piece = board[row][column];\r
3962 \r
3963       square_color = ((column + row) % 2) == 1;\r
3964       if(!strcmp(appData.variant, "xiangqi") ) {\r
3965           square_color = !InPalace(row, column);\r
3966           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3967           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3968       }\r
3969       piece_color = (int) piece < (int) BlackPawn;\r
3970 \r
3971 \r
3972       /* [HGM] holdings file: light square or black */\r
3973       if(column == BOARD_LEFT-2) {\r
3974             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3975                 square_color = 1;\r
3976             else {\r
3977                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3978                 continue;\r
3979             }\r
3980       } else\r
3981       if(column == BOARD_RGHT + 1 ) {\r
3982             if( row < gameInfo.holdingsSize )\r
3983                 square_color = 1;\r
3984             else {\r
3985                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3986                 continue;\r
3987             }\r
3988       }\r
3989       if(column == BOARD_LEFT-1 ) /* left align */\r
3990             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3991       else if( column == BOARD_RGHT) /* right align */\r
3992             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3993       else\r
3994       if (appData.monoMode) {\r
3995         if (piece == EmptySquare) {\r
3996           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3997                  square_color ? WHITENESS : BLACKNESS);\r
3998         } else {\r
3999           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4000         }\r
4001       } \r
4002       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4003           /* [AS] Draw the square using a texture bitmap */\r
4004           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4005 \r
4006           DrawTile( x, y, \r
4007               squareSize, squareSize, \r
4008               hdc, \r
4009               texture_hdc,\r
4010               backTextureSquareInfo[row][column].mode,\r
4011               backTextureSquareInfo[row][column].x,\r
4012               backTextureSquareInfo[row][column].y );\r
4013 \r
4014           SelectObject( texture_hdc, hbm );\r
4015 \r
4016           if (piece != EmptySquare) {\r
4017               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4018           }\r
4019       }\r
4020       else {\r
4021         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4022 \r
4023         oldBrush = SelectObject(hdc, brush );\r
4024         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4025         SelectObject(hdc, oldBrush);\r
4026         if (piece != EmptySquare)\r
4027           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4028       }\r
4029     }\r
4030   }\r
4031 \r
4032   if( texture_hdc != NULL ) {\r
4033     DeleteDC( texture_hdc );\r
4034   }\r
4035 }\r
4036 \r
4037 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4038 void fputDW(FILE *f, int x)\r
4039 {\r
4040         fputc(x     & 255, f);\r
4041         fputc(x>>8  & 255, f);\r
4042         fputc(x>>16 & 255, f);\r
4043         fputc(x>>24 & 255, f);\r
4044 }\r
4045 \r
4046 #define MAX_CLIPS 200   /* more than enough */\r
4047 \r
4048 VOID\r
4049 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4050 {\r
4051   static Board lastReq, lastDrawn;\r
4052   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4053   static int lastDrawnFlipView = 0;\r
4054   static int lastReqValid = 0, lastDrawnValid = 0;\r
4055   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4056   HDC tmphdc;\r
4057   HDC hdcmem;\r
4058   HBITMAP bufferBitmap;\r
4059   HBITMAP oldBitmap;\r
4060   RECT Rect;\r
4061   HRGN clips[MAX_CLIPS];\r
4062   ChessSquare dragged_piece = EmptySquare;\r
4063 \r
4064   /* I'm undecided on this - this function figures out whether a full\r
4065    * repaint is necessary on its own, so there's no real reason to have the\r
4066    * caller tell it that.  I think this can safely be set to FALSE - but\r
4067    * if we trust the callers not to request full repaints unnessesarily, then\r
4068    * we could skip some clipping work.  In other words, only request a full\r
4069    * redraw when the majority of pieces have changed positions (ie. flip, \r
4070    * gamestart and similar)  --Hawk\r
4071    */\r
4072   Boolean fullrepaint = repaint;\r
4073 \r
4074   if( DrawPositionNeedsFullRepaint() ) {\r
4075       fullrepaint = TRUE;\r
4076   }\r
4077 \r
4078 #if 0\r
4079   if( fullrepaint ) {\r
4080       static int repaint_count = 0;\r
4081       char buf[128];\r
4082 \r
4083       repaint_count++;\r
4084       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4085       OutputDebugString( buf );\r
4086   }\r
4087 #endif\r
4088 \r
4089   if (board == NULL) {\r
4090     if (!lastReqValid) {\r
4091       return;\r
4092     }\r
4093     board = lastReq;\r
4094   } else {\r
4095     CopyBoard(lastReq, board);\r
4096     lastReqValid = 1;\r
4097   }\r
4098 \r
4099   if (doingSizing) {\r
4100     return;\r
4101   }\r
4102 \r
4103   if (IsIconic(hwndMain)) {\r
4104     return;\r
4105   }\r
4106 \r
4107   if (hdc == NULL) {\r
4108     hdc = GetDC(hwndMain);\r
4109     if (!appData.monoMode) {\r
4110       SelectPalette(hdc, hPal, FALSE);\r
4111       RealizePalette(hdc);\r
4112     }\r
4113     releaseDC = TRUE;\r
4114   } else {\r
4115     releaseDC = FALSE;\r
4116   }\r
4117 \r
4118 #if 0\r
4119   fprintf(debugFP, "*******************************\n"\r
4120                    "repaint = %s\n"\r
4121                    "dragInfo.from (%d,%d)\n"\r
4122                    "dragInfo.start (%d,%d)\n"\r
4123                    "dragInfo.pos (%d,%d)\n"\r
4124                    "dragInfo.lastpos (%d,%d)\n", \r
4125                     repaint ? "TRUE" : "FALSE",\r
4126                     dragInfo.from.x, dragInfo.from.y, \r
4127                     dragInfo.start.x, dragInfo.start.y,\r
4128                     dragInfo.pos.x, dragInfo.pos.y,\r
4129                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4130   fprintf(debugFP, "prev:  ");\r
4131   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4132     for (column = 0; column < BOARD_WIDTH; column++) {\r
4133       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4134     }\r
4135   }\r
4136   fprintf(debugFP, "\n");\r
4137   fprintf(debugFP, "board: ");\r
4138   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4139     for (column = 0; column < BOARD_WIDTH; column++) {\r
4140       fprintf(debugFP, "%d ", board[row][column]);\r
4141     }\r
4142   }\r
4143   fprintf(debugFP, "\n");\r
4144   fflush(debugFP);\r
4145 #endif\r
4146 \r
4147   /* Create some work-DCs */\r
4148   hdcmem = CreateCompatibleDC(hdc);\r
4149   tmphdc = CreateCompatibleDC(hdc);\r
4150 \r
4151   /* If dragging is in progress, we temporarely remove the piece */\r
4152   /* [HGM] or temporarily decrease count if stacked              */\r
4153   /*       !! Moved to before board compare !!                   */\r
4154   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4155     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4156     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4157             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4158         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4159     } else \r
4160     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4161             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4162         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4163     } else \r
4164         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4165   }\r
4166 \r
4167   /* Figure out which squares need updating by comparing the \r
4168    * newest board with the last drawn board and checking if\r
4169    * flipping has changed.\r
4170    */\r
4171   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4172     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4173       for (column = 0; column < BOARD_WIDTH; column++) {\r
4174         if (lastDrawn[row][column] != board[row][column]) {\r
4175           SquareToPos(row, column, &x, &y);\r
4176           clips[num_clips++] =\r
4177             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4178         }\r
4179       }\r
4180     }\r
4181     for (i=0; i<2; i++) {\r
4182       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4183           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4184         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4185             lastDrawnHighlight.sq[i].y >= 0) {\r
4186           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4187                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4188           clips[num_clips++] =\r
4189             CreateRectRgn(x - lineGap, y - lineGap, \r
4190                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4191         }\r
4192         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4193           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4194           clips[num_clips++] =\r
4195             CreateRectRgn(x - lineGap, y - lineGap, \r
4196                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4197         }\r
4198       }\r
4199     }\r
4200     for (i=0; i<2; i++) {\r
4201       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4202           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4203         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4204             lastDrawnPremove.sq[i].y >= 0) {\r
4205           SquareToPos(lastDrawnPremove.sq[i].y,\r
4206                       lastDrawnPremove.sq[i].x, &x, &y);\r
4207           clips[num_clips++] =\r
4208             CreateRectRgn(x - lineGap, y - lineGap, \r
4209                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4210         }\r
4211         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4212             premoveHighlightInfo.sq[i].y >= 0) {\r
4213           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4214                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4215           clips[num_clips++] =\r
4216             CreateRectRgn(x - lineGap, y - lineGap, \r
4217                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4218         }\r
4219       }\r
4220     }\r
4221   } else {\r
4222     fullrepaint = TRUE;\r
4223   }\r
4224 \r
4225   /* Create a buffer bitmap - this is the actual bitmap\r
4226    * being written to.  When all the work is done, we can\r
4227    * copy it to the real DC (the screen).  This avoids\r
4228    * the problems with flickering.\r
4229    */\r
4230   GetClientRect(hwndMain, &Rect);\r
4231   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4232                                         Rect.bottom-Rect.top+1);\r
4233   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4234   if (!appData.monoMode) {\r
4235     SelectPalette(hdcmem, hPal, FALSE);\r
4236   }\r
4237 \r
4238   /* Create clips for dragging */\r
4239   if (!fullrepaint) {\r
4240     if (dragInfo.from.x >= 0) {\r
4241       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4242       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4243     }\r
4244     if (dragInfo.start.x >= 0) {\r
4245       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4246       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4247     }\r
4248     if (dragInfo.pos.x >= 0) {\r
4249       x = dragInfo.pos.x - squareSize / 2;\r
4250       y = dragInfo.pos.y - squareSize / 2;\r
4251       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4252     }\r
4253     if (dragInfo.lastpos.x >= 0) {\r
4254       x = dragInfo.lastpos.x - squareSize / 2;\r
4255       y = dragInfo.lastpos.y - squareSize / 2;\r
4256       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4257     }\r
4258   }\r
4259 \r
4260   /* Are we animating a move?  \r
4261    * If so, \r
4262    *   - remove the piece from the board (temporarely)\r
4263    *   - calculate the clipping region\r
4264    */\r
4265   if (!fullrepaint) {\r
4266     if (animInfo.piece != EmptySquare) {\r
4267       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4268       x = boardRect.left + animInfo.lastpos.x;\r
4269       y = boardRect.top + animInfo.lastpos.y;\r
4270       x2 = boardRect.left + animInfo.pos.x;\r
4271       y2 = boardRect.top + animInfo.pos.y;\r
4272       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4273       /* Slight kludge.  The real problem is that after AnimateMove is\r
4274          done, the position on the screen does not match lastDrawn.\r
4275          This currently causes trouble only on e.p. captures in\r
4276          atomic, where the piece moves to an empty square and then\r
4277          explodes.  The old and new positions both had an empty square\r
4278          at the destination, but animation has drawn a piece there and\r
4279          we have to remember to erase it. */\r
4280       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4281     }\r
4282   }\r
4283 \r
4284   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4285   if (num_clips == 0)\r
4286     fullrepaint = TRUE;\r
4287 \r
4288   /* Set clipping on the memory DC */\r
4289   if (!fullrepaint) {\r
4290     SelectClipRgn(hdcmem, clips[0]);\r
4291     for (x = 1; x < num_clips; x++) {\r
4292       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4293         abort();  // this should never ever happen!\r
4294     }\r
4295   }\r
4296 \r
4297   /* Do all the drawing to the memory DC */\r
4298   DrawGridOnDC(hdcmem);\r
4299   DrawHighlightsOnDC(hdcmem);\r
4300   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4301 \r
4302   if( appData.highlightMoveWithArrow ) {\r
4303     DrawArrowHighlight(hdcmem);\r
4304   }\r
4305 \r
4306   DrawCoordsOnDC(hdcmem);\r
4307 \r
4308   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4309                  /* to make sure lastDrawn contains what is actually drawn */\r
4310 \r
4311   /* Put the dragged piece back into place and draw it (out of place!) */\r
4312     if (dragged_piece != EmptySquare) {\r
4313     /* [HGM] or restack */\r
4314     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4315                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4316     else\r
4317     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4318                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4319     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4320     x = dragInfo.pos.x - squareSize / 2;\r
4321     y = dragInfo.pos.y - squareSize / 2;\r
4322     DrawPieceOnDC(hdcmem, dragged_piece,\r
4323                   ((int) dragged_piece < (int) BlackPawn), \r
4324                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4325   }   \r
4326   \r
4327   /* Put the animated piece back into place and draw it */\r
4328   if (animInfo.piece != EmptySquare) {\r
4329     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4330     x = boardRect.left + animInfo.pos.x;\r
4331     y = boardRect.top + animInfo.pos.y;\r
4332     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4333                   ((int) animInfo.piece < (int) BlackPawn),\r
4334                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4335   }\r
4336 \r
4337   /* Release the bufferBitmap by selecting in the old bitmap \r
4338    * and delete the memory DC\r
4339    */\r
4340   SelectObject(hdcmem, oldBitmap);\r
4341   DeleteDC(hdcmem);\r
4342 \r
4343   /* Set clipping on the target DC */\r
4344   if (!fullrepaint) {\r
4345     SelectClipRgn(hdc, clips[0]);\r
4346     for (x = 1; x < num_clips; x++) {\r
4347       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4348         abort();   // this should never ever happen!\r
4349     } \r
4350   }\r
4351 \r
4352   /* Copy the new bitmap onto the screen in one go.\r
4353    * This way we avoid any flickering\r
4354    */\r
4355   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4356   BitBlt(hdc, boardRect.left, boardRect.top,\r
4357          boardRect.right - boardRect.left,\r
4358          boardRect.bottom - boardRect.top,\r
4359          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4360 \r
4361   if(saveDiagFlag) { \r
4362     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4363     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4364 \r
4365     GetObject(bufferBitmap, sizeof(b), &b);\r
4366     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4367         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4368         bih.biWidth = b.bmWidth;\r
4369         bih.biHeight = b.bmHeight;\r
4370         bih.biPlanes = 1;\r
4371         bih.biBitCount = b.bmBitsPixel;\r
4372         bih.biCompression = 0;\r
4373         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4374         bih.biXPelsPerMeter = 0;\r
4375         bih.biYPelsPerMeter = 0;\r
4376         bih.biClrUsed = 0;\r
4377         bih.biClrImportant = 0;\r
4378 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4379 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4380         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4381 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4382 \r
4383 #if 1\r
4384         wb = b.bmWidthBytes;\r
4385         // count colors\r
4386         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4387                 int k = ((int*) pData)[i];\r
4388                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4389                 if(j >= 16) break;\r
4390                 color[j] = k;\r
4391                 if(j >= nrColors) nrColors = j+1;\r
4392         }\r
4393         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4394                 INT p = 0;\r
4395                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4396                     for(w=0; w<(wb>>2); w+=2) {\r
4397                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4398                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4399                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4400                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4401                         pData[p++] = m | j<<4;\r
4402                     }\r
4403                     while(p&3) pData[p++] = 0;\r
4404                 }\r
4405                 fac = 3;\r
4406                 wb = (wb+31>>5)<<2;\r
4407         }\r
4408         // write BITMAPFILEHEADER\r
4409         fprintf(diagFile, "BM");\r
4410         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4411         fputDW(diagFile, 0);\r
4412         fputDW(diagFile, 0x36 + (fac?64:0));\r
4413         // write BITMAPINFOHEADER\r
4414         fputDW(diagFile, 40);\r
4415         fputDW(diagFile, b.bmWidth);\r
4416         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4417         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4418         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4419         fputDW(diagFile, 0);\r
4420         fputDW(diagFile, 0);\r
4421         fputDW(diagFile, 0);\r
4422         fputDW(diagFile, 0);\r
4423         fputDW(diagFile, 0);\r
4424         fputDW(diagFile, 0);\r
4425         // write color table\r
4426         if(fac)\r
4427         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4428         // write bitmap data\r
4429         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4430                 fputc(pData[i], diagFile);\r
4431 #endif\r
4432      }\r
4433   }\r
4434 \r
4435   SelectObject(tmphdc, oldBitmap);\r
4436 \r
4437   /* Massive cleanup */\r
4438   for (x = 0; x < num_clips; x++)\r
4439     DeleteObject(clips[x]);\r
4440 \r
4441   DeleteDC(tmphdc);\r
4442   DeleteObject(bufferBitmap);\r
4443 \r
4444   if (releaseDC) \r
4445     ReleaseDC(hwndMain, hdc);\r
4446   \r
4447   if (lastDrawnFlipView != flipView) {\r
4448     if (flipView)\r
4449       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4450     else\r
4451       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4452   }\r
4453 \r
4454 /*  CopyBoard(lastDrawn, board);*/\r
4455   lastDrawnHighlight = highlightInfo;\r
4456   lastDrawnPremove   = premoveHighlightInfo;\r
4457   lastDrawnFlipView = flipView;\r
4458   lastDrawnValid = 1;\r
4459 }\r
4460 \r
4461 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4462 int\r
4463 SaveDiagram(f)\r
4464      FILE *f;\r
4465 {\r
4466     time_t tm;\r
4467     char *fen;\r
4468 \r
4469     saveDiagFlag = 1; diagFile = f;\r
4470     HDCDrawPosition(NULL, TRUE, NULL);\r
4471 \r
4472     saveDiagFlag = 0;\r
4473 \r
4474 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4475     \r
4476     fclose(f);\r
4477     return TRUE;\r
4478 }\r
4479 \r
4480 \r
4481 /*---------------------------------------------------------------------------*\\r
4482 | CLIENT PAINT PROCEDURE\r
4483 |   This is the main event-handler for the WM_PAINT message.\r
4484 |\r
4485 \*---------------------------------------------------------------------------*/\r
4486 VOID\r
4487 PaintProc(HWND hwnd)\r
4488 {\r
4489   HDC         hdc;\r
4490   PAINTSTRUCT ps;\r
4491   HFONT       oldFont;\r
4492 \r
4493   if(hdc = BeginPaint(hwnd, &ps)) {\r
4494     if (IsIconic(hwnd)) {\r
4495       DrawIcon(hdc, 2, 2, iconCurrent);\r
4496     } else {\r
4497       if (!appData.monoMode) {\r
4498         SelectPalette(hdc, hPal, FALSE);\r
4499         RealizePalette(hdc);\r
4500       }\r
4501       HDCDrawPosition(hdc, 1, NULL);\r
4502       oldFont =\r
4503         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4504       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4505                  ETO_CLIPPED|ETO_OPAQUE,\r
4506                  &messageRect, messageText, strlen(messageText), NULL);\r
4507       SelectObject(hdc, oldFont);\r
4508       DisplayBothClocks();\r
4509     }\r
4510     EndPaint(hwnd,&ps);\r
4511   }\r
4512 \r
4513   return;\r
4514 }\r
4515 \r
4516 \r
4517 /*\r
4518  * If the user selects on a border boundary, return -1; if off the board,\r
4519  *   return -2.  Otherwise map the event coordinate to the square.\r
4520  * The offset boardRect.left or boardRect.top must already have been\r
4521  *   subtracted from x.\r
4522  */\r
4523 int\r
4524 EventToSquare(int x)\r
4525 {\r
4526   if (x <= 0)\r
4527     return -2;\r
4528   if (x < lineGap)\r
4529     return -1;\r
4530   x -= lineGap;\r
4531   if ((x % (squareSize + lineGap)) >= squareSize)\r
4532     return -1;\r
4533   x /= (squareSize + lineGap);\r
4534   if (x >= BOARD_SIZE)\r
4535     return -2;\r
4536   return x;\r
4537 }\r
4538 \r
4539 typedef struct {\r
4540   char piece;\r
4541   int command;\r
4542   char* name;\r
4543 } DropEnable;\r
4544 \r
4545 DropEnable dropEnables[] = {\r
4546   { 'P', DP_Pawn, "Pawn" },\r
4547   { 'N', DP_Knight, "Knight" },\r
4548   { 'B', DP_Bishop, "Bishop" },\r
4549   { 'R', DP_Rook, "Rook" },\r
4550   { 'Q', DP_Queen, "Queen" },\r
4551 };\r
4552 \r
4553 VOID\r
4554 SetupDropMenu(HMENU hmenu)\r
4555 {\r
4556   int i, count, enable;\r
4557   char *p;\r
4558   extern char white_holding[], black_holding[];\r
4559   char item[MSG_SIZ];\r
4560 \r
4561   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4562     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4563                dropEnables[i].piece);\r
4564     count = 0;\r
4565     while (p && *p++ == dropEnables[i].piece) count++;\r
4566     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4567     enable = count > 0 || !appData.testLegality\r
4568       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4569                       && !appData.icsActive);\r
4570     ModifyMenu(hmenu, dropEnables[i].command,\r
4571                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4572                dropEnables[i].command, item);\r
4573   }\r
4574 }\r
4575 \r
4576 static int fromX = -1, fromY = -1, toX, toY;\r
4577 \r
4578 /* Event handler for mouse messages */\r
4579 VOID\r
4580 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4581 {\r
4582   int x, y;\r
4583   POINT pt;\r
4584   static int recursive = 0;\r
4585   HMENU hmenu;\r
4586   BOOLEAN needsRedraw = FALSE;\r
4587   BOOLEAN saveAnimate;\r
4588   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4589   static BOOLEAN sameAgain = FALSE;\r
4590   ChessMove moveType;\r
4591 \r
4592   if (recursive) {\r
4593     if (message == WM_MBUTTONUP) {\r
4594       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4595          to the middle button: we simulate pressing the left button too!\r
4596          */\r
4597       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4598       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4599     }\r
4600     return;\r
4601   }\r
4602   recursive++;\r
4603   \r
4604   pt.x = LOWORD(lParam);\r
4605   pt.y = HIWORD(lParam);\r
4606   x = EventToSquare(pt.x - boardRect.left);\r
4607   y = EventToSquare(pt.y - boardRect.top);\r
4608   if (!flipView && y >= 0) {\r
4609     y = BOARD_HEIGHT - 1 - y;\r
4610   }\r
4611   if (flipView && x >= 0) {\r
4612     x = BOARD_WIDTH - 1 - x;\r
4613   }\r
4614 \r
4615   switch (message) {\r
4616   case WM_LBUTTONDOWN:\r
4617     ErrorPopDown();\r
4618     sameAgain = FALSE;\r
4619     if (y == -2) {\r
4620       /* Downclick vertically off board; check if on clock */\r
4621       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4622         if (gameMode == EditPosition) {\r
4623           SetWhiteToPlayEvent();\r
4624         } else if (gameMode == IcsPlayingBlack ||\r
4625                    gameMode == MachinePlaysWhite) {\r
4626           CallFlagEvent();\r
4627         } else if (gameMode == EditGame) {\r
4628           AdjustClock(flipClock, -1);\r
4629         }\r
4630       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4631         if (gameMode == EditPosition) {\r
4632           SetBlackToPlayEvent();\r
4633         } else if (gameMode == IcsPlayingWhite ||\r
4634                    gameMode == MachinePlaysBlack) {\r
4635           CallFlagEvent();\r
4636         } else if (gameMode == EditGame) {\r
4637           AdjustClock(!flipClock, -1);\r
4638         }\r
4639       }\r
4640       if (!appData.highlightLastMove) {\r
4641         ClearHighlights();\r
4642         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4643       }\r
4644       fromX = fromY = -1;\r
4645       dragInfo.start.x = dragInfo.start.y = -1;\r
4646       dragInfo.from = dragInfo.start;\r
4647       break;\r
4648     } else if (x < 0 || y < 0\r
4649       /* [HGM] block clicks between board and holdings */\r
4650               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4651               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4652               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4653         /* EditPosition, empty square, or different color piece;\r
4654            click-click move is possible */\r
4655                                ) {\r
4656       break;\r
4657     } else if (fromX == x && fromY == y) {\r
4658       /* Downclick on same square again */\r
4659       ClearHighlights();\r
4660       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4661       sameAgain = TRUE;  \r
4662     } else if (fromX != -1 &&\r
4663                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4664                                                                         ) {\r
4665       /* Downclick on different square. */\r
4666       /* [HGM] if on holdings file, should count as new first click ! */\r
4667       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4668         toX = x;\r
4669         toY = y;\r
4670         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4671            to make sure move is legal before showing promotion popup */\r
4672         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4673         if(moveType != ImpossibleMove) {\r
4674           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4675           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4676              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4677               appData.alwaysPromoteToQueen) {\r
4678                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4679                   if (!appData.highlightLastMove) {\r
4680                       ClearHighlights();\r
4681                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4682                   }\r
4683           } else\r
4684           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4685                   SetHighlights(fromX, fromY, toX, toY);\r
4686                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4687                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4688                      If promotion to Q is legal, all are legal! */\r
4689                   PromotionPopup(hwnd);\r
4690           } else {       /* not a promotion */\r
4691              if (appData.animate || appData.highlightLastMove) {\r
4692                  SetHighlights(fromX, fromY, toX, toY);\r
4693              } else {\r
4694                  ClearHighlights();\r
4695              }\r
4696              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4697              if (appData.animate && !appData.highlightLastMove) {\r
4698                   ClearHighlights();\r
4699                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4700              }\r
4701           }\r
4702           break;\r
4703         }\r
4704         if (gotPremove) {\r
4705             /* [HGM] it seemed that braces were missing here */\r
4706             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4707             fromX = fromY = -1;\r
4708             break;\r
4709         }\r
4710       }\r
4711       ClearHighlights();\r
4712       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4713     }\r
4714     /* First downclick, or restart on a square with same color piece */\r
4715     if (!frozen && OKToStartUserMove(x, y)) {\r
4716       fromX = x;\r
4717       fromY = y;\r
4718       dragInfo.lastpos = pt;\r
4719       dragInfo.from.x = fromX;\r
4720       dragInfo.from.y = fromY;\r
4721       dragInfo.start = dragInfo.from;\r
4722       SetCapture(hwndMain);\r
4723     } else {\r
4724       fromX = fromY = -1;\r
4725       dragInfo.start.x = dragInfo.start.y = -1;\r
4726       dragInfo.from = dragInfo.start;\r
4727       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4728     }\r
4729     break;\r
4730 \r
4731   case WM_LBUTTONUP:\r
4732     ReleaseCapture();\r
4733     if (fromX == -1) break;\r
4734     if (x == fromX && y == fromY) {\r
4735       dragInfo.from.x = dragInfo.from.y = -1;\r
4736       /* Upclick on same square */\r
4737       if (sameAgain) {\r
4738         /* Clicked same square twice: abort click-click move */\r
4739         fromX = fromY = -1;\r
4740         gotPremove = 0;\r
4741         ClearPremoveHighlights();\r
4742       } else {\r
4743         /* First square clicked: start click-click move */\r
4744         SetHighlights(fromX, fromY, -1, -1);\r
4745       }\r
4746       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4747     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4748       /* Errant click; ignore */\r
4749       break;\r
4750     } else {\r
4751       /* Finish drag move. */\r
4752     if (appData.debugMode) {\r
4753         fprintf(debugFP, "release\n");\r
4754     }\r
4755       dragInfo.from.x = dragInfo.from.y = -1;\r
4756       toX = x;\r
4757       toY = y;\r
4758       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4759       appData.animate = appData.animate && !appData.animateDragging;\r
4760       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4761       if(moveType != ImpossibleMove) {\r
4762           /* [HGM] use move type to determine if move is promotion.\r
4763              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4764           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4765              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4766               appData.alwaysPromoteToQueen) \r
4767                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4768           else \r
4769           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4770                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4771                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4772         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4773       }\r
4774       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4775       appData.animate = saveAnimate;\r
4776       fromX = fromY = -1;\r
4777       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4778         ClearHighlights();\r
4779       }\r
4780       if (appData.animate || appData.animateDragging ||\r
4781           appData.highlightDragging || gotPremove) {\r
4782         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4783       }\r
4784     }\r
4785     dragInfo.start.x = dragInfo.start.y = -1; \r
4786     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4787     break;\r
4788 \r
4789   case WM_MOUSEMOVE:\r
4790     if ((appData.animateDragging || appData.highlightDragging)\r
4791         && (wParam & MK_LBUTTON)\r
4792         && dragInfo.from.x >= 0) \r
4793     {\r
4794       BOOL full_repaint = FALSE;\r
4795 \r
4796       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4797       if (appData.animateDragging) {\r
4798         dragInfo.pos = pt;\r
4799       }\r
4800       if (appData.highlightDragging) {\r
4801         SetHighlights(fromX, fromY, x, y);\r
4802         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4803             full_repaint = TRUE;\r
4804         }\r
4805       }\r
4806       \r
4807       DrawPosition( full_repaint, NULL);\r
4808       \r
4809       dragInfo.lastpos = dragInfo.pos;\r
4810     }\r
4811     break;\r
4812 \r
4813   case WM_MBUTTONDOWN:\r
4814   case WM_RBUTTONDOWN:\r
4815     ErrorPopDown();\r
4816     ReleaseCapture();\r
4817     fromX = fromY = -1;\r
4818     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4819     dragInfo.start.x = dragInfo.start.y = -1;\r
4820     dragInfo.from = dragInfo.start;\r
4821     dragInfo.lastpos = dragInfo.pos;\r
4822     if (appData.highlightDragging) {\r
4823       ClearHighlights();\r
4824     }\r
4825     if(y == -2) {\r
4826       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4827       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4828           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4829       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4830           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4831       }\r
4832     }\r
4833     DrawPosition(TRUE, NULL);\r
4834 \r
4835     switch (gameMode) {\r
4836     case EditPosition:\r
4837     case IcsExamining:\r
4838       if (x < 0 || y < 0) break;\r
4839       fromX = x;\r
4840       fromY = y;\r
4841       if (message == WM_MBUTTONDOWN) {\r
4842         buttonCount = 3;  /* even if system didn't think so */\r
4843         if (wParam & MK_SHIFT) \r
4844           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4845         else\r
4846           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4847       } else { /* message == WM_RBUTTONDOWN */\r
4848 #if 0\r
4849         if (buttonCount == 3) {\r
4850           if (wParam & MK_SHIFT) \r
4851             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4852           else\r
4853             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4854         } else {\r
4855           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4856         }\r
4857 #else\r
4858         /* Just have one menu, on the right button.  Windows users don't\r
4859            think to try the middle one, and sometimes other software steals\r
4860            it, or it doesn't really exist. */\r
4861         if(gameInfo.variant != VariantShogi)\r
4862             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4863         else\r
4864             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4865 #endif\r
4866       }\r
4867       break;\r
4868     case IcsPlayingWhite:\r
4869     case IcsPlayingBlack:\r
4870     case EditGame:\r
4871     case MachinePlaysWhite:\r
4872     case MachinePlaysBlack:\r
4873       if (appData.testLegality &&\r
4874           gameInfo.variant != VariantBughouse &&\r
4875           gameInfo.variant != VariantCrazyhouse) break;\r
4876       if (x < 0 || y < 0) break;\r
4877       fromX = x;\r
4878       fromY = y;\r
4879       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4880       SetupDropMenu(hmenu);\r
4881       MenuPopup(hwnd, pt, hmenu, -1);\r
4882       break;\r
4883     default:\r
4884       break;\r
4885     }\r
4886     break;\r
4887   }\r
4888 \r
4889   recursive--;\r
4890 }\r
4891 \r
4892 /* Preprocess messages for buttons in main window */\r
4893 LRESULT CALLBACK\r
4894 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4895 {\r
4896   int id = GetWindowLong(hwnd, GWL_ID);\r
4897   int i, dir;\r
4898 \r
4899   for (i=0; i<N_BUTTONS; i++) {\r
4900     if (buttonDesc[i].id == id) break;\r
4901   }\r
4902   if (i == N_BUTTONS) return 0;\r
4903   switch (message) {\r
4904   case WM_KEYDOWN:\r
4905     switch (wParam) {\r
4906     case VK_LEFT:\r
4907     case VK_RIGHT:\r
4908       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4909       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4910       return TRUE;\r
4911     }\r
4912     break;\r
4913   case WM_CHAR:\r
4914     switch (wParam) {\r
4915     case '\r':\r
4916       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4917       return TRUE;\r
4918     case '\t':\r
4919       if (appData.icsActive) {\r
4920         if (GetKeyState(VK_SHIFT) < 0) {\r
4921           /* shifted */\r
4922           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4923           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4924           SetFocus(h);\r
4925         } else {\r
4926           /* unshifted */\r
4927           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4928           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4929           SetFocus(h);\r
4930         }\r
4931         return TRUE;\r
4932       }\r
4933       break;\r
4934     default:\r
4935       if (appData.icsActive) {\r
4936         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4937         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4938         SetFocus(h);\r
4939         SendMessage(h, WM_CHAR, wParam, lParam);\r
4940         return TRUE;\r
4941       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4942         PopUpMoveDialog((char)wParam);\r
4943       }\r
4944       break;\r
4945     }\r
4946     break;\r
4947   }\r
4948   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4949 }\r
4950 \r
4951 /* Process messages for Promotion dialog box */\r
4952 LRESULT CALLBACK\r
4953 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4954 {\r
4955   char promoChar;\r
4956 \r
4957   switch (message) {\r
4958   case WM_INITDIALOG: /* message: initialize dialog box */\r
4959     /* Center the dialog over the application window */\r
4960     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4961     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4962       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4963        gameInfo.variant == VariantGiveaway) ?\r
4964                SW_SHOW : SW_HIDE);\r
4965     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4966     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4967        (PieceToChar(WhiteAngel) >= 'A' &&\r
4968         PieceToChar(WhiteAngel) != '~' ||\r
4969         PieceToChar(BlackAngel) >= 'A' &&\r
4970         PieceToChar(BlackAngel) != '~'   ) ?\r
4971                SW_SHOW : SW_HIDE);\r
4972     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4973        (PieceToChar(WhiteMarshall) >= 'A' &&\r
4974         PieceToChar(WhiteMarshall) != '~' ||\r
4975         PieceToChar(BlackMarshall) >= 'A' &&\r
4976         PieceToChar(BlackMarshall) != '~'   ) ?\r
4977                SW_SHOW : SW_HIDE);\r
4978     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4979     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4980        gameInfo.variant != VariantShogi ?\r
4981                SW_SHOW : SW_HIDE);\r
4982     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4983        gameInfo.variant != VariantShogi ?\r
4984                SW_SHOW : SW_HIDE);\r
4985     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4986        gameInfo.variant == VariantShogi ?\r
4987                SW_SHOW : SW_HIDE);\r
4988     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4989        gameInfo.variant == VariantShogi ?\r
4990                SW_SHOW : SW_HIDE);\r
4991     return TRUE;\r
4992 \r
4993   case WM_COMMAND: /* message: received a command */\r
4994     switch (LOWORD(wParam)) {\r
4995     case IDCANCEL:\r
4996       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4997       ClearHighlights();\r
4998       DrawPosition(FALSE, NULL);\r
4999       return TRUE;\r
5000     case PB_King:\r
5001       promoChar = PieceToChar(BlackKing);\r
5002       break;\r
5003     case PB_Queen:\r
5004       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5005       break;\r
5006     case PB_Rook:\r
5007       promoChar = PieceToChar(BlackRook);\r
5008       break;\r
5009     case PB_Bishop:\r
5010       promoChar = PieceToChar(BlackBishop);\r
5011       break;\r
5012     case PB_Chancellor:\r
5013       promoChar = PieceToChar(BlackMarshall);\r
5014       break;\r
5015     case PB_Archbishop:\r
5016       promoChar = PieceToChar(BlackAngel);\r
5017       break;\r
5018     case PB_Knight:\r
5019       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5020       break;\r
5021     default:\r
5022       return FALSE;\r
5023     }\r
5024     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5025     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5026        only show the popup when we are already sure the move is valid or\r
5027        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5028        will figure out it is a promotion from the promoChar. */\r
5029     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5030     if (!appData.highlightLastMove) {\r
5031       ClearHighlights();\r
5032       DrawPosition(FALSE, NULL);\r
5033     }\r
5034     return TRUE;\r
5035   }\r
5036   return FALSE;\r
5037 }\r
5038 \r
5039 /* Pop up promotion dialog */\r
5040 VOID\r
5041 PromotionPopup(HWND hwnd)\r
5042 {\r
5043   FARPROC lpProc;\r
5044 \r
5045   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5046   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5047     hwnd, (DLGPROC)lpProc);\r
5048   FreeProcInstance(lpProc);\r
5049 }\r
5050 \r
5051 /* Toggle ShowThinking */\r
5052 VOID\r
5053 ToggleShowThinking()\r
5054 {\r
5055   ShowThinkingEvent(!appData.showThinking);\r
5056 }\r
5057 \r
5058 VOID\r
5059 LoadGameDialog(HWND hwnd, char* title)\r
5060 {\r
5061   UINT number = 0;\r
5062   FILE *f;\r
5063   char fileTitle[MSG_SIZ];\r
5064   f = OpenFileDialog(hwnd, "rb", "",\r
5065                      appData.oldSaveStyle ? "gam" : "pgn",\r
5066                      GAME_FILT,\r
5067                      title, &number, fileTitle, NULL);\r
5068   if (f != NULL) {\r
5069     cmailMsgLoaded = FALSE;\r
5070     if (number == 0) {\r
5071       int error = GameListBuild(f);\r
5072       if (error) {\r
5073         DisplayError("Cannot build game list", error);\r
5074       } else if (!ListEmpty(&gameList) &&\r
5075                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5076         GameListPopUp(f, fileTitle);\r
5077         return;\r
5078       }\r
5079       GameListDestroy();\r
5080       number = 1;\r
5081     }\r
5082     LoadGame(f, number, fileTitle, FALSE);\r
5083   }\r
5084 }\r
5085 \r
5086 VOID\r
5087 ChangedConsoleFont()\r
5088 {\r
5089   CHARFORMAT cfmt;\r
5090   CHARRANGE tmpsel, sel;\r
5091   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5092   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5093   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5094   PARAFORMAT paraf;\r
5095 \r
5096   cfmt.cbSize = sizeof(CHARFORMAT);\r
5097   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5098   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5099   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5100    * size.  This was undocumented in the version of MSVC++ that I had\r
5101    * when I wrote the code, but is apparently documented now.\r
5102    */\r
5103   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5104   cfmt.bCharSet = f->lf.lfCharSet;\r
5105   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5106   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5107   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5108   /* Why are the following seemingly needed too? */\r
5109   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5110   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5111   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5112   tmpsel.cpMin = 0;\r
5113   tmpsel.cpMax = -1; /*999999?*/\r
5114   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5115   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5116   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5117    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5118    */\r
5119   paraf.cbSize = sizeof(paraf);\r
5120   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5121   paraf.dxStartIndent = 0;\r
5122   paraf.dxOffset = WRAP_INDENT;\r
5123   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5124   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5125 }\r
5126 \r
5127 /*---------------------------------------------------------------------------*\\r
5128  *\r
5129  * Window Proc for main window\r
5130  *\r
5131 \*---------------------------------------------------------------------------*/\r
5132 \r
5133 /* Process messages for main window, etc. */\r
5134 LRESULT CALLBACK\r
5135 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5136 {\r
5137   FARPROC lpProc;\r
5138   int wmId, wmEvent;\r
5139   char *defName;\r
5140   FILE *f;\r
5141   UINT number;\r
5142   char fileTitle[MSG_SIZ];\r
5143   static SnapData sd;\r
5144 \r
5145   switch (message) {\r
5146 \r
5147   case WM_PAINT: /* message: repaint portion of window */\r
5148     PaintProc(hwnd);\r
5149     break;\r
5150 \r
5151   case WM_ERASEBKGND:\r
5152     if (IsIconic(hwnd)) {\r
5153       /* Cheat; change the message */\r
5154       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5155     } else {\r
5156       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5157     }\r
5158     break;\r
5159 \r
5160   case WM_LBUTTONDOWN:\r
5161   case WM_MBUTTONDOWN:\r
5162   case WM_RBUTTONDOWN:\r
5163   case WM_LBUTTONUP:\r
5164   case WM_MBUTTONUP:\r
5165   case WM_RBUTTONUP:\r
5166   case WM_MOUSEMOVE:\r
5167     MouseEvent(hwnd, message, wParam, lParam);\r
5168     break;\r
5169 \r
5170   case WM_CHAR:\r
5171     \r
5172     if (appData.icsActive) {\r
5173       if (wParam == '\t') {\r
5174         if (GetKeyState(VK_SHIFT) < 0) {\r
5175           /* shifted */\r
5176           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5177           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5178           SetFocus(h);\r
5179         } else {\r
5180           /* unshifted */\r
5181           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5182           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5183           SetFocus(h);\r
5184         }\r
5185       } else {\r
5186         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5187         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5188         SetFocus(h);\r
5189         SendMessage(h, message, wParam, lParam);\r
5190       }\r
5191     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5192       PopUpMoveDialog((char)wParam);\r
5193     }\r
5194     break;\r
5195 \r
5196   case WM_PALETTECHANGED:\r
5197     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5198       int nnew;\r
5199       HDC hdc = GetDC(hwndMain);\r
5200       SelectPalette(hdc, hPal, TRUE);\r
5201       nnew = RealizePalette(hdc);\r
5202       if (nnew > 0) {\r
5203         paletteChanged = TRUE;\r
5204 #if 0\r
5205         UpdateColors(hdc);\r
5206 #else\r
5207         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5208 #endif\r
5209       }\r
5210       ReleaseDC(hwnd, hdc);\r
5211     }\r
5212     break;\r
5213 \r
5214   case WM_QUERYNEWPALETTE:\r
5215     if (!appData.monoMode /*&& paletteChanged*/) {\r
5216       int nnew;\r
5217       HDC hdc = GetDC(hwndMain);\r
5218       paletteChanged = FALSE;\r
5219       SelectPalette(hdc, hPal, FALSE);\r
5220       nnew = RealizePalette(hdc);\r
5221       if (nnew > 0) {\r
5222         InvalidateRect(hwnd, &boardRect, FALSE);\r
5223       }\r
5224       ReleaseDC(hwnd, hdc);\r
5225       return TRUE;\r
5226     }\r
5227     return FALSE;\r
5228 \r
5229   case WM_COMMAND: /* message: command from application menu */\r
5230     wmId    = LOWORD(wParam);\r
5231     wmEvent = HIWORD(wParam);\r
5232 \r
5233     switch (wmId) {\r
5234     case IDM_NewGame:\r
5235       ResetGameEvent();\r
5236       AnalysisPopDown();\r
5237       break;\r
5238 \r
5239     case IDM_NewGameFRC:\r
5240       if( NewGameFRC() == 0 ) {\r
5241         ResetGameEvent();\r
5242         AnalysisPopDown();\r
5243       }\r
5244       break;\r
5245 \r
5246     case IDM_NewVariant:\r
5247       NewVariantPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_LoadGame:\r
5251       LoadGameDialog(hwnd, "Load Game from File");\r
5252       break;\r
5253 \r
5254     case IDM_LoadNextGame:\r
5255       ReloadGame(1);\r
5256       break;\r
5257 \r
5258     case IDM_LoadPrevGame:\r
5259       ReloadGame(-1);\r
5260       break;\r
5261 \r
5262     case IDM_ReloadGame:\r
5263       ReloadGame(0);\r
5264       break;\r
5265 \r
5266     case IDM_LoadPosition:\r
5267       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5268         Reset(FALSE, TRUE);\r
5269       }\r
5270       number = 1;\r
5271       f = OpenFileDialog(hwnd, "rb", "",\r
5272                          appData.oldSaveStyle ? "pos" : "fen",\r
5273                          POSITION_FILT,\r
5274                          "Load Position from File", &number, fileTitle, NULL);\r
5275       if (f != NULL) {\r
5276         LoadPosition(f, number, fileTitle);\r
5277       }\r
5278       break;\r
5279 \r
5280     case IDM_LoadNextPosition:\r
5281       ReloadPosition(1);\r
5282       break;\r
5283 \r
5284     case IDM_LoadPrevPosition:\r
5285       ReloadPosition(-1);\r
5286       break;\r
5287 \r
5288     case IDM_ReloadPosition:\r
5289       ReloadPosition(0);\r
5290       break;\r
5291 \r
5292     case IDM_SaveGame:\r
5293       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5294       f = OpenFileDialog(hwnd, "a", defName,\r
5295                          appData.oldSaveStyle ? "gam" : "pgn",\r
5296                          GAME_FILT,\r
5297                          "Save Game to File", NULL, fileTitle, NULL);\r
5298       if (f != NULL) {\r
5299         SaveGame(f, 0, "");\r
5300       }\r
5301       break;\r
5302 \r
5303     case IDM_SavePosition:\r
5304       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5305       f = OpenFileDialog(hwnd, "a", defName,\r
5306                          appData.oldSaveStyle ? "pos" : "fen",\r
5307                          POSITION_FILT,\r
5308                          "Save Position to File", NULL, fileTitle, NULL);\r
5309       if (f != NULL) {\r
5310         SavePosition(f, 0, "");\r
5311       }\r
5312       break;\r
5313 \r
5314     case IDM_SaveDiagram:\r
5315       defName = "diagram";\r
5316       f = OpenFileDialog(hwnd, "wb", defName,\r
5317                          "bmp",\r
5318                          DIAGRAM_FILT,\r
5319                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5320       if (f != NULL) {\r
5321         SaveDiagram(f);\r
5322       }\r
5323       break;\r
5324 \r
5325     case IDM_CopyGame:\r
5326       CopyGameToClipboard();\r
5327       break;\r
5328 \r
5329     case IDM_PasteGame:\r
5330       PasteGameFromClipboard();\r
5331       break;\r
5332 \r
5333     case IDM_CopyGameListToClipboard:\r
5334       CopyGameListToClipboard();\r
5335       break;\r
5336 \r
5337     /* [AS] Autodetect FEN or PGN data */\r
5338     case IDM_PasteAny:\r
5339       PasteGameOrFENFromClipboard();\r
5340       break;\r
5341 \r
5342     /* [AS] Move history */\r
5343     case IDM_ShowMoveHistory:\r
5344         if( MoveHistoryIsUp() ) {\r
5345             MoveHistoryPopDown();\r
5346         }\r
5347         else {\r
5348             MoveHistoryPopUp();\r
5349         }\r
5350         break;\r
5351 \r
5352     /* [AS] Eval graph */\r
5353     case IDM_ShowEvalGraph:\r
5354         if( EvalGraphIsUp() ) {\r
5355             EvalGraphPopDown();\r
5356         }\r
5357         else {\r
5358             EvalGraphPopUp();\r
5359         }\r
5360         break;\r
5361 \r
5362     /* [AS] Engine output */\r
5363     case IDM_ShowEngineOutput:\r
5364         if( EngineOutputIsUp() ) {\r
5365             EngineOutputPopDown();\r
5366         }\r
5367         else {\r
5368             EngineOutputPopUp();\r
5369         }\r
5370         break;\r
5371 \r
5372     /* [AS] User adjudication */\r
5373     case IDM_UserAdjudication_White:\r
5374         UserAdjudicationEvent( +1 );\r
5375         break;\r
5376 \r
5377     case IDM_UserAdjudication_Black:\r
5378         UserAdjudicationEvent( -1 );\r
5379         break;\r
5380 \r
5381     case IDM_UserAdjudication_Draw:\r
5382         UserAdjudicationEvent( 0 );\r
5383         break;\r
5384 \r
5385     /* [AS] Game list options dialog */\r
5386     case IDM_GameListOptions:\r
5387       GameListOptions();\r
5388       break;\r
5389 \r
5390     case IDM_CopyPosition:\r
5391       CopyFENToClipboard();\r
5392       break;\r
5393 \r
5394     case IDM_PastePosition:\r
5395       PasteFENFromClipboard();\r
5396       break;\r
5397 \r
5398     case IDM_MailMove:\r
5399       MailMoveEvent();\r
5400       break;\r
5401 \r
5402     case IDM_ReloadCMailMsg:\r
5403       Reset(TRUE, TRUE);\r
5404       ReloadCmailMsgEvent(FALSE);\r
5405       break;\r
5406 \r
5407     case IDM_Minimize:\r
5408       ShowWindow(hwnd, SW_MINIMIZE);\r
5409       break;\r
5410 \r
5411     case IDM_Exit:\r
5412       ExitEvent(0);\r
5413       break;\r
5414 \r
5415     case IDM_MachineWhite:\r
5416       MachineWhiteEvent();\r
5417       /*\r
5418        * refresh the tags dialog only if it's visible\r
5419        */\r
5420       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5421           char *tags;\r
5422           tags = PGNTags(&gameInfo);\r
5423           TagsPopUp(tags, CmailMsg());\r
5424           free(tags);\r
5425       }\r
5426       break;\r
5427 \r
5428     case IDM_MachineBlack:\r
5429       MachineBlackEvent();\r
5430       /*\r
5431        * refresh the tags dialog only if it's visible\r
5432        */\r
5433       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5434           char *tags;\r
5435           tags = PGNTags(&gameInfo);\r
5436           TagsPopUp(tags, CmailMsg());\r
5437           free(tags);\r
5438       }\r
5439       break;\r
5440 \r
5441     case IDM_TwoMachines:\r
5442       TwoMachinesEvent();\r
5443       /*\r
5444        * refresh the tags dialog only if it's visible\r
5445        */\r
5446       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5447           char *tags;\r
5448           tags = PGNTags(&gameInfo);\r
5449           TagsPopUp(tags, CmailMsg());\r
5450           free(tags);\r
5451       }\r
5452       break;\r
5453 \r
5454     case IDM_AnalysisMode:\r
5455       if (!first.analysisSupport) {\r
5456         char buf[MSG_SIZ];\r
5457         sprintf(buf, "%s does not support analysis", first.tidy);\r
5458         DisplayError(buf, 0);\r
5459       } else {\r
5460         if (!appData.showThinking) ToggleShowThinking();\r
5461         AnalyzeModeEvent();\r
5462       }\r
5463       break;\r
5464 \r
5465     case IDM_AnalyzeFile:\r
5466       if (!first.analysisSupport) {\r
5467         char buf[MSG_SIZ];\r
5468         sprintf(buf, "%s does not support analysis", first.tidy);\r
5469         DisplayError(buf, 0);\r
5470       } else {\r
5471         if (!appData.showThinking) ToggleShowThinking();\r
5472         AnalyzeFileEvent();\r
5473         LoadGameDialog(hwnd, "Analyze Game from File");\r
5474         AnalysisPeriodicEvent(1);\r
5475       }\r
5476       break;\r
5477 \r
5478     case IDM_IcsClient:\r
5479       IcsClientEvent();\r
5480       break;\r
5481 \r
5482     case IDM_EditGame:\r
5483       EditGameEvent();\r
5484       break;\r
5485 \r
5486     case IDM_EditPosition:\r
5487       EditPositionEvent();\r
5488       break;\r
5489 \r
5490     case IDM_Training:\r
5491       TrainingEvent();\r
5492       break;\r
5493 \r
5494     case IDM_ShowGameList:\r
5495       ShowGameListProc();\r
5496       break;\r
5497 \r
5498     case IDM_EditTags:\r
5499       EditTagsProc();\r
5500       break;\r
5501 \r
5502     case IDM_EditComment:\r
5503       if (commentDialogUp && editComment) {\r
5504         CommentPopDown();\r
5505       } else {\r
5506         EditCommentEvent();\r
5507       }\r
5508       break;\r
5509 \r
5510     case IDM_Pause:\r
5511       PauseEvent();\r
5512       break;\r
5513 \r
5514     case IDM_Accept:\r
5515       AcceptEvent();\r
5516       break;\r
5517 \r
5518     case IDM_Decline:\r
5519       DeclineEvent();\r
5520       break;\r
5521 \r
5522     case IDM_Rematch:\r
5523       RematchEvent();\r
5524       break;\r
5525 \r
5526     case IDM_CallFlag:\r
5527       CallFlagEvent();\r
5528       break;\r
5529 \r
5530     case IDM_Draw:\r
5531       DrawEvent();\r
5532       break;\r
5533 \r
5534     case IDM_Adjourn:\r
5535       AdjournEvent();\r
5536       break;\r
5537 \r
5538     case IDM_Abort:\r
5539       AbortEvent();\r
5540       break;\r
5541 \r
5542     case IDM_Resign:\r
5543       ResignEvent();\r
5544       break;\r
5545 \r
5546     case IDM_StopObserving:\r
5547       StopObservingEvent();\r
5548       break;\r
5549 \r
5550     case IDM_StopExamining:\r
5551       StopExaminingEvent();\r
5552       break;\r
5553 \r
5554     case IDM_TypeInMove:\r
5555       PopUpMoveDialog('\000');\r
5556       break;\r
5557 \r
5558     case IDM_TypeInName:\r
5559       PopUpNameDialog('\000');\r
5560       break;\r
5561 \r
5562     case IDM_Backward:\r
5563       BackwardEvent();\r
5564       SetFocus(hwndMain);\r
5565       break;\r
5566 \r
5567     case IDM_Forward:\r
5568       ForwardEvent();\r
5569       SetFocus(hwndMain);\r
5570       break;\r
5571 \r
5572     case IDM_ToStart:\r
5573       ToStartEvent();\r
5574       SetFocus(hwndMain);\r
5575       break;\r
5576 \r
5577     case IDM_ToEnd:\r
5578       ToEndEvent();\r
5579       SetFocus(hwndMain);\r
5580       break;\r
5581 \r
5582     case IDM_Revert:\r
5583       RevertEvent();\r
5584       break;\r
5585 \r
5586     case IDM_TruncateGame:\r
5587       TruncateGameEvent();\r
5588       break;\r
5589 \r
5590     case IDM_MoveNow:\r
5591       MoveNowEvent();\r
5592       break;\r
5593 \r
5594     case IDM_RetractMove:\r
5595       RetractMoveEvent();\r
5596       break;\r
5597 \r
5598     case IDM_FlipView:\r
5599       flipView = !flipView;\r
5600       DrawPosition(FALSE, NULL);\r
5601       break;\r
5602 \r
5603     case IDM_FlipClock:\r
5604       flipClock = !flipClock;\r
5605       DisplayBothClocks();\r
5606       break;\r
5607 \r
5608     case IDM_GeneralOptions:\r
5609       GeneralOptionsPopup(hwnd);\r
5610       DrawPosition(TRUE, NULL);\r
5611       break;\r
5612 \r
5613     case IDM_BoardOptions:\r
5614       BoardOptionsPopup(hwnd);\r
5615       break;\r
5616 \r
5617     case IDM_EnginePlayOptions:\r
5618       EnginePlayOptionsPopup(hwnd);\r
5619       break;\r
5620 \r
5621     case IDM_OptionsUCI:\r
5622       UciOptionsPopup(hwnd);\r
5623       break;\r
5624 \r
5625     case IDM_IcsOptions:\r
5626       IcsOptionsPopup(hwnd);\r
5627       break;\r
5628 \r
5629     case IDM_Fonts:\r
5630       FontsOptionsPopup(hwnd);\r
5631       break;\r
5632 \r
5633     case IDM_Sounds:\r
5634       SoundOptionsPopup(hwnd);\r
5635       break;\r
5636 \r
5637     case IDM_CommPort:\r
5638       CommPortOptionsPopup(hwnd);\r
5639       break;\r
5640 \r
5641     case IDM_LoadOptions:\r
5642       LoadOptionsPopup(hwnd);\r
5643       break;\r
5644 \r
5645     case IDM_SaveOptions:\r
5646       SaveOptionsPopup(hwnd);\r
5647       break;\r
5648 \r
5649     case IDM_TimeControl:\r
5650       TimeControlOptionsPopup(hwnd);\r
5651       break;\r
5652 \r
5653     case IDM_SaveSettings:\r
5654       SaveSettings(settingsFileName);\r
5655       break;\r
5656 \r
5657     case IDM_SaveSettingsOnExit:\r
5658       saveSettingsOnExit = !saveSettingsOnExit;\r
5659       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5660                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5661                                          MF_CHECKED : MF_UNCHECKED));\r
5662       break;\r
5663 \r
5664     case IDM_Hint:\r
5665       HintEvent();\r
5666       break;\r
5667 \r
5668     case IDM_Book:\r
5669       BookEvent();\r
5670       break;\r
5671 \r
5672     case IDM_AboutGame:\r
5673       AboutGameEvent();\r
5674       break;\r
5675 \r
5676     case IDM_Debug:\r
5677       appData.debugMode = !appData.debugMode;\r
5678       if (appData.debugMode) {\r
5679         char dir[MSG_SIZ];\r
5680         GetCurrentDirectory(MSG_SIZ, dir);\r
5681         SetCurrentDirectory(installDir);\r
5682         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5683         SetCurrentDirectory(dir);\r
5684         setbuf(debugFP, NULL);\r
5685       } else {\r
5686         fclose(debugFP);\r
5687         debugFP = NULL;\r
5688       }\r
5689       break;\r
5690 \r
5691     case IDM_HELPCONTENTS:\r
5692       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5693         MessageBox (GetFocus(),\r
5694                     "Unable to activate help",\r
5695                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5696       }\r
5697       break;\r
5698 \r
5699     case IDM_HELPSEARCH:\r
5700       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5701         MessageBox (GetFocus(),\r
5702                     "Unable to activate help",\r
5703                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5704       }\r
5705       break;\r
5706 \r
5707     case IDM_HELPHELP:\r
5708       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5709         MessageBox (GetFocus(),\r
5710                     "Unable to activate help",\r
5711                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5712       }\r
5713       break;\r
5714 \r
5715     case IDM_ABOUT:\r
5716       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5717       DialogBox(hInst, \r
5718         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5719         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5720       FreeProcInstance(lpProc);\r
5721       break;\r
5722 \r
5723     case IDM_DirectCommand1:\r
5724       AskQuestionEvent("Direct Command",\r
5725                        "Send to chess program:", "", "1");\r
5726       break;\r
5727     case IDM_DirectCommand2:\r
5728       AskQuestionEvent("Direct Command",\r
5729                        "Send to second chess program:", "", "2");\r
5730       break;\r
5731 \r
5732     case EP_WhitePawn:\r
5733       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5734       fromX = fromY = -1;\r
5735       break;\r
5736 \r
5737     case EP_WhiteKnight:\r
5738       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5739       fromX = fromY = -1;\r
5740       break;\r
5741 \r
5742     case EP_WhiteBishop:\r
5743       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5744       fromX = fromY = -1;\r
5745       break;\r
5746 \r
5747     case EP_WhiteRook:\r
5748       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5749       fromX = fromY = -1;\r
5750       break;\r
5751 \r
5752     case EP_WhiteQueen:\r
5753       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5754       fromX = fromY = -1;\r
5755       break;\r
5756 \r
5757     case EP_WhiteFerz:\r
5758       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5759       fromX = fromY = -1;\r
5760       break;\r
5761 \r
5762     case EP_WhiteWazir:\r
5763       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5764       fromX = fromY = -1;\r
5765       break;\r
5766 \r
5767     case EP_WhiteAlfil:\r
5768       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5769       fromX = fromY = -1;\r
5770       break;\r
5771 \r
5772     case EP_WhiteCannon:\r
5773       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5774       fromX = fromY = -1;\r
5775       break;\r
5776 \r
5777     case EP_WhiteCardinal:\r
5778       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5779       fromX = fromY = -1;\r
5780       break;\r
5781 \r
5782     case EP_WhiteMarshall:\r
5783       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5784       fromX = fromY = -1;\r
5785       break;\r
5786 \r
5787     case EP_WhiteKing:\r
5788       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5789       fromX = fromY = -1;\r
5790       break;\r
5791 \r
5792     case EP_BlackPawn:\r
5793       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5794       fromX = fromY = -1;\r
5795       break;\r
5796 \r
5797     case EP_BlackKnight:\r
5798       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5799       fromX = fromY = -1;\r
5800       break;\r
5801 \r
5802     case EP_BlackBishop:\r
5803       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5804       fromX = fromY = -1;\r
5805       break;\r
5806 \r
5807     case EP_BlackRook:\r
5808       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5809       fromX = fromY = -1;\r
5810       break;\r
5811 \r
5812     case EP_BlackQueen:\r
5813       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5814       fromX = fromY = -1;\r
5815       break;\r
5816 \r
5817     case EP_BlackFerz:\r
5818       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5819       fromX = fromY = -1;\r
5820       break;\r
5821 \r
5822     case EP_BlackWazir:\r
5823       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5824       fromX = fromY = -1;\r
5825       break;\r
5826 \r
5827     case EP_BlackAlfil:\r
5828       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5829       fromX = fromY = -1;\r
5830       break;\r
5831 \r
5832     case EP_BlackCannon:\r
5833       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5834       fromX = fromY = -1;\r
5835       break;\r
5836 \r
5837     case EP_BlackCardinal:\r
5838       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5839       fromX = fromY = -1;\r
5840       break;\r
5841 \r
5842     case EP_BlackMarshall:\r
5843       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5844       fromX = fromY = -1;\r
5845       break;\r
5846 \r
5847     case EP_BlackKing:\r
5848       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5849       fromX = fromY = -1;\r
5850       break;\r
5851 \r
5852     case EP_EmptySquare:\r
5853       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5854       fromX = fromY = -1;\r
5855       break;\r
5856 \r
5857     case EP_ClearBoard:\r
5858       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5859       fromX = fromY = -1;\r
5860       break;\r
5861 \r
5862     case EP_White:\r
5863       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5864       fromX = fromY = -1;\r
5865       break;\r
5866 \r
5867     case EP_Black:\r
5868       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5869       fromX = fromY = -1;\r
5870       break;\r
5871 \r
5872     case EP_Promote:\r
5873       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5874       fromX = fromY = -1;\r
5875       break;\r
5876 \r
5877     case EP_Demote:\r
5878       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5879       fromX = fromY = -1;\r
5880       break;\r
5881 \r
5882     case DP_Pawn:\r
5883       DropMenuEvent(WhitePawn, fromX, fromY);\r
5884       fromX = fromY = -1;\r
5885       break;\r
5886 \r
5887     case DP_Knight:\r
5888       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5889       fromX = fromY = -1;\r
5890       break;\r
5891 \r
5892     case DP_Bishop:\r
5893       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5894       fromX = fromY = -1;\r
5895       break;\r
5896 \r
5897     case DP_Rook:\r
5898       DropMenuEvent(WhiteRook, fromX, fromY);\r
5899       fromX = fromY = -1;\r
5900       break;\r
5901 \r
5902     case DP_Queen:\r
5903       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5904       fromX = fromY = -1;\r
5905       break;\r
5906 \r
5907     default:\r
5908       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5909     }\r
5910     break;\r
5911 \r
5912   case WM_TIMER:\r
5913     switch (wParam) {\r
5914     case CLOCK_TIMER_ID:\r
5915       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5916       clockTimerEvent = 0;\r
5917       DecrementClocks(); /* call into back end */\r
5918       break;\r
5919     case LOAD_GAME_TIMER_ID:\r
5920       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5921       loadGameTimerEvent = 0;\r
5922       AutoPlayGameLoop(); /* call into back end */\r
5923       break;\r
5924     case ANALYSIS_TIMER_ID:\r
5925       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5926           appData.periodicUpdates) {\r
5927         AnalysisPeriodicEvent(0);\r
5928       } else {\r
5929         KillTimer(hwnd, analysisTimerEvent);\r
5930         analysisTimerEvent = 0;\r
5931       }\r
5932       break;\r
5933     case DELAYED_TIMER_ID:\r
5934       KillTimer(hwnd, delayedTimerEvent);\r
5935       delayedTimerEvent = 0;\r
5936       delayedTimerCallback();\r
5937       break;\r
5938     }\r
5939     break;\r
5940 \r
5941   case WM_USER_Input:\r
5942     InputEvent(hwnd, message, wParam, lParam);\r
5943     break;\r
5944 \r
5945   /* [AS] Also move "attached" child windows */\r
5946   case WM_WINDOWPOSCHANGING:\r
5947     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5948         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5949 \r
5950         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5951             /* Window is moving */\r
5952             RECT rcMain;\r
5953 \r
5954             GetWindowRect( hwnd, &rcMain );\r
5955             \r
5956             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5957             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5958             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5959         }\r
5960     }\r
5961     break;\r
5962 \r
5963   /* [AS] Snapping */\r
5964   case WM_ENTERSIZEMOVE:\r
5965     if (hwnd == hwndMain) {\r
5966       doingSizing = TRUE;\r
5967       lastSizing = 0;\r
5968     }\r
5969     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5970     break;\r
5971 \r
5972   case WM_SIZING:\r
5973     if (hwnd == hwndMain) {\r
5974       lastSizing = wParam;\r
5975     }\r
5976     break;\r
5977 \r
5978   case WM_MOVING:\r
5979       return OnMoving( &sd, hwnd, wParam, lParam );\r
5980 \r
5981   case WM_EXITSIZEMOVE:\r
5982     if (hwnd == hwndMain) {\r
5983       RECT client;\r
5984       doingSizing = FALSE;\r
5985       InvalidateRect(hwnd, &boardRect, FALSE);\r
5986       GetClientRect(hwnd, &client);\r
5987       ResizeBoard(client.right, client.bottom, lastSizing);\r
5988       lastSizing = 0;\r
5989     }\r
5990     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5991     break;\r
5992 \r
5993   case WM_DESTROY: /* message: window being destroyed */\r
5994     PostQuitMessage(0);\r
5995     break;\r
5996 \r
5997   case WM_CLOSE:\r
5998     if (hwnd == hwndMain) {\r
5999       ExitEvent(0);\r
6000     }\r
6001     break;\r
6002 \r
6003   default:      /* Passes it on if unprocessed */\r
6004     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6005   }\r
6006   return 0;\r
6007 }\r
6008 \r
6009 /*---------------------------------------------------------------------------*\\r
6010  *\r
6011  * Misc utility routines\r
6012  *\r
6013 \*---------------------------------------------------------------------------*/\r
6014 \r
6015 /*\r
6016  * Decent random number generator, at least not as bad as Windows\r
6017  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6018  */\r
6019 unsigned int randstate;\r
6020 \r
6021 int\r
6022 myrandom(void)\r
6023 {\r
6024   randstate = randstate * 1664525 + 1013904223;\r
6025   return (int) randstate & 0x7fffffff;\r
6026 }\r
6027 \r
6028 void\r
6029 mysrandom(unsigned int seed)\r
6030 {\r
6031   randstate = seed;\r
6032 }\r
6033 \r
6034 \r
6035 /* \r
6036  * returns TRUE if user selects a different color, FALSE otherwise \r
6037  */\r
6038 \r
6039 BOOL\r
6040 ChangeColor(HWND hwnd, COLORREF *which)\r
6041 {\r
6042   static BOOL firstTime = TRUE;\r
6043   static DWORD customColors[16];\r
6044   CHOOSECOLOR cc;\r
6045   COLORREF newcolor;\r
6046   int i;\r
6047   ColorClass ccl;\r
6048 \r
6049   if (firstTime) {\r
6050     /* Make initial colors in use available as custom colors */\r
6051     /* Should we put the compiled-in defaults here instead? */\r
6052     i = 0;\r
6053     customColors[i++] = lightSquareColor & 0xffffff;\r
6054     customColors[i++] = darkSquareColor & 0xffffff;\r
6055     customColors[i++] = whitePieceColor & 0xffffff;\r
6056     customColors[i++] = blackPieceColor & 0xffffff;\r
6057     customColors[i++] = highlightSquareColor & 0xffffff;\r
6058     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6059 \r
6060     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6061       customColors[i++] = textAttribs[ccl].color;\r
6062     }\r
6063     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6064     firstTime = FALSE;\r
6065   }\r
6066 \r
6067   cc.lStructSize = sizeof(cc);\r
6068   cc.hwndOwner = hwnd;\r
6069   cc.hInstance = NULL;\r
6070   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6071   cc.lpCustColors = (LPDWORD) customColors;\r
6072   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6073 \r
6074   if (!ChooseColor(&cc)) return FALSE;\r
6075 \r
6076   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6077   if (newcolor == *which) return FALSE;\r
6078   *which = newcolor;\r
6079   return TRUE;\r
6080 \r
6081   /*\r
6082   InitDrawingColors();\r
6083   InvalidateRect(hwnd, &boardRect, FALSE);\r
6084   */\r
6085 }\r
6086 \r
6087 BOOLEAN\r
6088 MyLoadSound(MySound *ms)\r
6089 {\r
6090   BOOL ok = FALSE;\r
6091   struct stat st;\r
6092   FILE *f;\r
6093 \r
6094   if (ms->data) free(ms->data);\r
6095   ms->data = NULL;\r
6096 \r
6097   switch (ms->name[0]) {\r
6098   case NULLCHAR:\r
6099     /* Silence */\r
6100     ok = TRUE;\r
6101     break;\r
6102   case '$':\r
6103     /* System sound from Control Panel.  Don't preload here. */\r
6104     ok = TRUE;\r
6105     break;\r
6106   case '!':\r
6107     if (ms->name[1] == NULLCHAR) {\r
6108       /* "!" alone = silence */\r
6109       ok = TRUE;\r
6110     } else {\r
6111       /* Builtin wave resource.  Error if not found. */\r
6112       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6113       if (h == NULL) break;\r
6114       ms->data = (void *)LoadResource(hInst, h);\r
6115       if (h == NULL) break;\r
6116       ok = TRUE;\r
6117     }\r
6118     break;\r
6119   default:\r
6120     /* .wav file.  Error if not found. */\r
6121     f = fopen(ms->name, "rb");\r
6122     if (f == NULL) break;\r
6123     if (fstat(fileno(f), &st) < 0) break;\r
6124     ms->data = malloc(st.st_size);\r
6125     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6126     fclose(f);\r
6127     ok = TRUE;\r
6128     break;\r
6129   }\r
6130   if (!ok) {\r
6131     char buf[MSG_SIZ];\r
6132     sprintf(buf, "Error loading sound %s", ms->name);\r
6133     DisplayError(buf, GetLastError());\r
6134   }\r
6135   return ok;\r
6136 }\r
6137 \r
6138 BOOLEAN\r
6139 MyPlaySound(MySound *ms)\r
6140 {\r
6141   BOOLEAN ok = FALSE;\r
6142   switch (ms->name[0]) {\r
6143   case NULLCHAR:\r
6144     /* Silence */\r
6145     ok = TRUE;\r
6146     break;\r
6147   case '$':\r
6148     /* System sound from Control Panel (deprecated feature).\r
6149        "$" alone or an unset sound name gets default beep (still in use). */\r
6150     if (ms->name[1]) {\r
6151       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6152     }\r
6153     if (!ok) ok = MessageBeep(MB_OK);\r
6154     break; \r
6155   case '!':\r
6156     /* Builtin wave resource, or "!" alone for silence */\r
6157     if (ms->name[1]) {\r
6158       if (ms->data == NULL) return FALSE;\r
6159       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6160     } else {\r
6161       ok = TRUE;\r
6162     }\r
6163     break;\r
6164   default:\r
6165     /* .wav file.  Error if not found. */\r
6166     if (ms->data == NULL) return FALSE;\r
6167     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6168     break;\r
6169   }\r
6170   /* Don't print an error: this can happen innocently if the sound driver\r
6171      is busy; for instance, if another instance of WinBoard is playing\r
6172      a sound at about the same time. */\r
6173 #if 0\r
6174   if (!ok) {\r
6175     char buf[MSG_SIZ];\r
6176     sprintf(buf, "Error playing sound %s", ms->name);\r
6177     DisplayError(buf, GetLastError());\r
6178   }\r
6179 #endif\r
6180   return ok;\r
6181 }\r
6182 \r
6183 \r
6184 LRESULT CALLBACK\r
6185 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6186 {\r
6187   BOOL ok;\r
6188   OPENFILENAME *ofn;\r
6189   static UINT *number; /* gross that this is static */\r
6190 \r
6191   switch (message) {\r
6192   case WM_INITDIALOG: /* message: initialize dialog box */\r
6193     /* Center the dialog over the application window */\r
6194     ofn = (OPENFILENAME *) lParam;\r
6195     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6196       number = (UINT *) ofn->lCustData;\r
6197       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6198     } else {\r
6199       number = NULL;\r
6200     }\r
6201     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6202     return FALSE;  /* Allow for further processing */\r
6203 \r
6204   case WM_COMMAND:\r
6205     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6206       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6207     }\r
6208     return FALSE;  /* Allow for further processing */\r
6209   }\r
6210   return FALSE;\r
6211 }\r
6212 \r
6213 UINT APIENTRY\r
6214 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6215 {\r
6216   static UINT *number;\r
6217   OPENFILENAME *ofname;\r
6218   OFNOTIFY *ofnot;\r
6219   switch (uiMsg) {\r
6220   case WM_INITDIALOG:\r
6221     ofname = (OPENFILENAME *)lParam;\r
6222     number = (UINT *)(ofname->lCustData);\r
6223     break;\r
6224   case WM_NOTIFY:\r
6225     ofnot = (OFNOTIFY *)lParam;\r
6226     if (ofnot->hdr.code == CDN_FILEOK) {\r
6227       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6228     }\r
6229     break;\r
6230   }\r
6231   return 0;\r
6232 }\r
6233 \r
6234 \r
6235 FILE *\r
6236 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6237                char *nameFilt, char *dlgTitle, UINT *number,\r
6238                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6239 {\r
6240   OPENFILENAME openFileName;\r
6241   char buf1[MSG_SIZ];\r
6242   FILE *f;\r
6243 \r
6244   if (fileName == NULL) fileName = buf1;\r
6245   if (defName == NULL) {\r
6246     strcpy(fileName, "*.");\r
6247     strcat(fileName, defExt);\r
6248   } else {\r
6249     strcpy(fileName, defName);\r
6250   }\r
6251   if (fileTitle) strcpy(fileTitle, "");\r
6252   if (number) *number = 0;\r
6253 \r
6254   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6255   openFileName.hwndOwner         = hwnd;\r
6256   openFileName.hInstance         = (HANDLE) hInst;\r
6257   openFileName.lpstrFilter       = nameFilt;\r
6258   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6259   openFileName.nMaxCustFilter    = 0L;\r
6260   openFileName.nFilterIndex      = 1L;\r
6261   openFileName.lpstrFile         = fileName;\r
6262   openFileName.nMaxFile          = MSG_SIZ;\r
6263   openFileName.lpstrFileTitle    = fileTitle;\r
6264   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6265   openFileName.lpstrInitialDir   = NULL;\r
6266   openFileName.lpstrTitle        = dlgTitle;\r
6267   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6268     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6269     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6270     | (oldDialog ? 0 : OFN_EXPLORER);\r
6271   openFileName.nFileOffset       = 0;\r
6272   openFileName.nFileExtension    = 0;\r
6273   openFileName.lpstrDefExt       = defExt;\r
6274   openFileName.lCustData         = (LONG) number;\r
6275   openFileName.lpfnHook          = oldDialog ?\r
6276     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6277   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6278 \r
6279   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6280                         GetOpenFileName(&openFileName)) {\r
6281     /* open the file */\r
6282     f = fopen(openFileName.lpstrFile, write);\r
6283     if (f == NULL) {\r
6284       MessageBox(hwnd, "File open failed", NULL,\r
6285                  MB_OK|MB_ICONEXCLAMATION);\r
6286       return NULL;\r
6287     }\r
6288   } else {\r
6289     int err = CommDlgExtendedError();\r
6290     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6291     return FALSE;\r
6292   }\r
6293   return f;\r
6294 }\r
6295 \r
6296 \r
6297 \r
6298 VOID APIENTRY\r
6299 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6300 {\r
6301   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6302 \r
6303   /*\r
6304    * Get the first pop-up menu in the menu template. This is the\r
6305    * menu that TrackPopupMenu displays.\r
6306    */\r
6307   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6308 \r
6309   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6310 \r
6311   /*\r
6312    * TrackPopup uses screen coordinates, so convert the\r
6313    * coordinates of the mouse click to screen coordinates.\r
6314    */\r
6315   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6316 \r
6317   /* Draw and track the floating pop-up menu. */\r
6318   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6319                  pt.x, pt.y, 0, hwnd, NULL);\r
6320 \r
6321   /* Destroy the menu.*/\r
6322   DestroyMenu(hmenu);\r
6323 }\r
6324    \r
6325 typedef struct {\r
6326   HWND hDlg, hText;\r
6327   int sizeX, sizeY, newSizeX, newSizeY;\r
6328   HDWP hdwp;\r
6329 } ResizeEditPlusButtonsClosure;\r
6330 \r
6331 BOOL CALLBACK\r
6332 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6333 {\r
6334   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6335   RECT rect;\r
6336   POINT pt;\r
6337 \r
6338   if (hChild == cl->hText) return TRUE;\r
6339   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6340   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6341   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6342   ScreenToClient(cl->hDlg, &pt);\r
6343   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6344     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6345   return TRUE;\r
6346 }\r
6347 \r
6348 /* Resize a dialog that has a (rich) edit field filling most of\r
6349    the top, with a row of buttons below */\r
6350 VOID\r
6351 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6352 {\r
6353   RECT rectText;\r
6354   int newTextHeight, newTextWidth;\r
6355   ResizeEditPlusButtonsClosure cl;\r
6356   \r
6357   /*if (IsIconic(hDlg)) return;*/\r
6358   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6359   \r
6360   cl.hdwp = BeginDeferWindowPos(8);\r
6361 \r
6362   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6363   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6364   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6365   if (newTextHeight < 0) {\r
6366     newSizeY += -newTextHeight;\r
6367     newTextHeight = 0;\r
6368   }\r
6369   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6370     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6371 \r
6372   cl.hDlg = hDlg;\r
6373   cl.hText = hText;\r
6374   cl.sizeX = sizeX;\r
6375   cl.sizeY = sizeY;\r
6376   cl.newSizeX = newSizeX;\r
6377   cl.newSizeY = newSizeY;\r
6378   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6379 \r
6380   EndDeferWindowPos(cl.hdwp);\r
6381 }\r
6382 \r
6383 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6384 {\r
6385     RECT    rChild, rParent;\r
6386     int     wChild, hChild, wParent, hParent;\r
6387     int     wScreen, hScreen, xNew, yNew;\r
6388     HDC     hdc;\r
6389 \r
6390     /* Get the Height and Width of the child window */\r
6391     GetWindowRect (hwndChild, &rChild);\r
6392     wChild = rChild.right - rChild.left;\r
6393     hChild = rChild.bottom - rChild.top;\r
6394 \r
6395     /* Get the Height and Width of the parent window */\r
6396     GetWindowRect (hwndParent, &rParent);\r
6397     wParent = rParent.right - rParent.left;\r
6398     hParent = rParent.bottom - rParent.top;\r
6399 \r
6400     /* Get the display limits */\r
6401     hdc = GetDC (hwndChild);\r
6402     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6403     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6404     ReleaseDC(hwndChild, hdc);\r
6405 \r
6406     /* Calculate new X position, then adjust for screen */\r
6407     xNew = rParent.left + ((wParent - wChild) /2);\r
6408     if (xNew < 0) {\r
6409         xNew = 0;\r
6410     } else if ((xNew+wChild) > wScreen) {\r
6411         xNew = wScreen - wChild;\r
6412     }\r
6413 \r
6414     /* Calculate new Y position, then adjust for screen */\r
6415     if( mode == 0 ) {\r
6416         yNew = rParent.top  + ((hParent - hChild) /2);\r
6417     }\r
6418     else {\r
6419         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6420     }\r
6421 \r
6422     if (yNew < 0) {\r
6423         yNew = 0;\r
6424     } else if ((yNew+hChild) > hScreen) {\r
6425         yNew = hScreen - hChild;\r
6426     }\r
6427 \r
6428     /* Set it, and return */\r
6429     return SetWindowPos (hwndChild, NULL,\r
6430                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6431 }\r
6432 \r
6433 /* Center one window over another */\r
6434 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6435 {\r
6436     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6437 }\r
6438 \r
6439 /*---------------------------------------------------------------------------*\\r
6440  *\r
6441  * Startup Dialog functions\r
6442  *\r
6443 \*---------------------------------------------------------------------------*/\r
6444 void\r
6445 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6446 {\r
6447   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6448 \r
6449   while (*cd != NULL) {\r
6450     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6451     cd++;\r
6452   }\r
6453 }\r
6454 \r
6455 void\r
6456 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6457 {\r
6458   char buf1[ARG_MAX];\r
6459   int len;\r
6460 \r
6461   if (str[0] == '@') {\r
6462     FILE* f = fopen(str + 1, "r");\r
6463     if (f == NULL) {\r
6464       DisplayFatalError(str + 1, errno, 2);\r
6465       return;\r
6466     }\r
6467     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6468     fclose(f);\r
6469     buf1[len] = NULLCHAR;\r
6470     str = buf1;\r
6471   }\r
6472 \r
6473   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6474 \r
6475   for (;;) {\r
6476     char buf[MSG_SIZ];\r
6477     char *end = strchr(str, '\n');\r
6478     if (end == NULL) return;\r
6479     memcpy(buf, str, end - str);\r
6480     buf[end - str] = NULLCHAR;\r
6481     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6482     str = end + 1;\r
6483   }\r
6484 }\r
6485 \r
6486 void\r
6487 SetStartupDialogEnables(HWND hDlg)\r
6488 {\r
6489   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6490     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6491     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6492   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6493     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6494   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6495     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6496   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6497     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6498   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6499     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6500     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6501     IsDlgButtonChecked(hDlg, OPT_View));\r
6502 }\r
6503 \r
6504 char *\r
6505 QuoteForFilename(char *filename)\r
6506 {\r
6507   int dquote, space;\r
6508   dquote = strchr(filename, '"') != NULL;\r
6509   space = strchr(filename, ' ') != NULL;\r
6510   if (dquote || space) {\r
6511     if (dquote) {\r
6512       return "'";\r
6513     } else {\r
6514       return "\"";\r
6515     }\r
6516   } else {\r
6517     return "";\r
6518   }\r
6519 }\r
6520 \r
6521 VOID\r
6522 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6523 {\r
6524   char buf[MSG_SIZ];\r
6525   char *q;\r
6526 \r
6527   InitComboStringsFromOption(hwndCombo, nthnames);\r
6528   q = QuoteForFilename(nthcp);\r
6529   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6530   if (*nthdir != NULLCHAR) {\r
6531     q = QuoteForFilename(nthdir);\r
6532     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6533   }\r
6534   if (*nthcp == NULLCHAR) {\r
6535     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6536   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6537     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6538     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6539   }\r
6540 }\r
6541 \r
6542 LRESULT CALLBACK\r
6543 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6544 {\r
6545   char buf[MSG_SIZ];\r
6546   HANDLE hwndCombo;\r
6547   char *p;\r
6548 \r
6549   switch (message) {\r
6550   case WM_INITDIALOG:\r
6551     /* Center the dialog */\r
6552     CenterWindow (hDlg, GetDesktopWindow());\r
6553     /* Initialize the dialog items */\r
6554     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6555                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6556                   firstChessProgramNames);\r
6557     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6558                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6559                   secondChessProgramNames);\r
6560     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6561     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6562     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6563     if (*appData.icsHelper != NULLCHAR) {\r
6564       char *q = QuoteForFilename(appData.icsHelper);\r
6565       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6566     }\r
6567     if (*appData.icsHost == NULLCHAR) {\r
6568       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6569       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6570     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6571       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6572       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6573     }\r
6574 \r
6575     if (appData.icsActive) {\r
6576       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6577     }\r
6578     else if (appData.noChessProgram) {\r
6579       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6580     }\r
6581     else {\r
6582       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6583     }\r
6584 \r
6585     SetStartupDialogEnables(hDlg);\r
6586     return TRUE;\r
6587 \r
6588   case WM_COMMAND:\r
6589     switch (LOWORD(wParam)) {\r
6590     case IDOK:\r
6591       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6592         strcpy(buf, "/fcp=");\r
6593         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6594         p = buf;\r
6595         ParseArgs(StringGet, &p);\r
6596         strcpy(buf, "/scp=");\r
6597         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6598         p = buf;\r
6599         ParseArgs(StringGet, &p);\r
6600         appData.noChessProgram = FALSE;\r
6601         appData.icsActive = FALSE;\r
6602       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6603         strcpy(buf, "/ics /icshost=");\r
6604         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6605         p = buf;\r
6606         ParseArgs(StringGet, &p);\r
6607         if (appData.zippyPlay) {\r
6608           strcpy(buf, "/fcp=");\r
6609           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6610           p = buf;\r
6611           ParseArgs(StringGet, &p);\r
6612         }\r
6613       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6614         appData.noChessProgram = TRUE;\r
6615         appData.icsActive = FALSE;\r
6616       } else {\r
6617         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6618                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6619         return TRUE;\r
6620       }\r
6621       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6622         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6623         p = buf;\r
6624         ParseArgs(StringGet, &p);\r
6625       }\r
6626       EndDialog(hDlg, TRUE);\r
6627       return TRUE;\r
6628 \r
6629     case IDCANCEL:\r
6630       ExitEvent(0);\r
6631       return TRUE;\r
6632 \r
6633     case IDM_HELPCONTENTS:\r
6634       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6635         MessageBox (GetFocus(),\r
6636                     "Unable to activate help",\r
6637                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6638       }\r
6639       break;\r
6640 \r
6641     default:\r
6642       SetStartupDialogEnables(hDlg);\r
6643       break;\r
6644     }\r
6645     break;\r
6646   }\r
6647   return FALSE;\r
6648 }\r
6649 \r
6650 /*---------------------------------------------------------------------------*\\r
6651  *\r
6652  * About box dialog functions\r
6653  *\r
6654 \*---------------------------------------------------------------------------*/\r
6655 \r
6656 /* Process messages for "About" dialog box */\r
6657 LRESULT CALLBACK\r
6658 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6659 {\r
6660   switch (message) {\r
6661   case WM_INITDIALOG: /* message: initialize dialog box */\r
6662     /* Center the dialog over the application window */\r
6663     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6664     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6665     return (TRUE);\r
6666 \r
6667   case WM_COMMAND: /* message: received a command */\r
6668     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6669         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6670       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6671       return (TRUE);\r
6672     }\r
6673     break;\r
6674   }\r
6675   return (FALSE);\r
6676 }\r
6677 \r
6678 /*---------------------------------------------------------------------------*\\r
6679  *\r
6680  * Comment Dialog functions\r
6681  *\r
6682 \*---------------------------------------------------------------------------*/\r
6683 \r
6684 LRESULT CALLBACK\r
6685 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6686 {\r
6687   static HANDLE hwndText = NULL;\r
6688   int len, newSizeX, newSizeY, flags;\r
6689   static int sizeX, sizeY;\r
6690   char *str;\r
6691   RECT rect;\r
6692   MINMAXINFO *mmi;\r
6693 \r
6694   switch (message) {\r
6695   case WM_INITDIALOG: /* message: initialize dialog box */\r
6696     /* Initialize the dialog items */\r
6697     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6698     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6699     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6700     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6701     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6702     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6703     SetWindowText(hDlg, commentTitle);\r
6704     if (editComment) {\r
6705       SetFocus(hwndText);\r
6706     } else {\r
6707       SetFocus(GetDlgItem(hDlg, IDOK));\r
6708     }\r
6709     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6710                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6711                 MAKELPARAM(FALSE, 0));\r
6712     /* Size and position the dialog */\r
6713     if (!commentDialog) {\r
6714       commentDialog = hDlg;\r
6715       flags = SWP_NOZORDER;\r
6716       GetClientRect(hDlg, &rect);\r
6717       sizeX = rect.right;\r
6718       sizeY = rect.bottom;\r
6719       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6720           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6721         WINDOWPLACEMENT wp;\r
6722         EnsureOnScreen(&commentX, &commentY);\r
6723         wp.length = sizeof(WINDOWPLACEMENT);\r
6724         wp.flags = 0;\r
6725         wp.showCmd = SW_SHOW;\r
6726         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6727         wp.rcNormalPosition.left = commentX;\r
6728         wp.rcNormalPosition.right = commentX + commentW;\r
6729         wp.rcNormalPosition.top = commentY;\r
6730         wp.rcNormalPosition.bottom = commentY + commentH;\r
6731         SetWindowPlacement(hDlg, &wp);\r
6732 \r
6733         GetClientRect(hDlg, &rect);\r
6734         newSizeX = rect.right;\r
6735         newSizeY = rect.bottom;\r
6736         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6737                               newSizeX, newSizeY);\r
6738         sizeX = newSizeX;\r
6739         sizeY = newSizeY;\r
6740       }\r
6741     }\r
6742     return FALSE;\r
6743 \r
6744   case WM_COMMAND: /* message: received a command */\r
6745     switch (LOWORD(wParam)) {\r
6746     case IDOK:\r
6747       if (editComment) {\r
6748         char *p, *q;\r
6749         /* Read changed options from the dialog box */\r
6750         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6751         len = GetWindowTextLength(hwndText);\r
6752         str = (char *) malloc(len + 1);\r
6753         GetWindowText(hwndText, str, len + 1);\r
6754         p = q = str;\r
6755         while (*q) {\r
6756           if (*q == '\r')\r
6757             q++;\r
6758           else\r
6759             *p++ = *q++;\r
6760         }\r
6761         *p = NULLCHAR;\r
6762         ReplaceComment(commentIndex, str);\r
6763         free(str);\r
6764       }\r
6765       CommentPopDown();\r
6766       return TRUE;\r
6767 \r
6768     case IDCANCEL:\r
6769     case OPT_CancelComment:\r
6770       CommentPopDown();\r
6771       return TRUE;\r
6772 \r
6773     case OPT_ClearComment:\r
6774       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6775       break;\r
6776 \r
6777     case OPT_EditComment:\r
6778       EditCommentEvent();\r
6779       return TRUE;\r
6780 \r
6781     default:\r
6782       break;\r
6783     }\r
6784     break;\r
6785 \r
6786   case WM_SIZE:\r
6787     newSizeX = LOWORD(lParam);\r
6788     newSizeY = HIWORD(lParam);\r
6789     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6790     sizeX = newSizeX;\r
6791     sizeY = newSizeY;\r
6792     break;\r
6793 \r
6794   case WM_GETMINMAXINFO:\r
6795     /* Prevent resizing window too small */\r
6796     mmi = (MINMAXINFO *) lParam;\r
6797     mmi->ptMinTrackSize.x = 100;\r
6798     mmi->ptMinTrackSize.y = 100;\r
6799     break;\r
6800   }\r
6801   return FALSE;\r
6802 }\r
6803 \r
6804 VOID\r
6805 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6806 {\r
6807   FARPROC lpProc;\r
6808   char *p, *q;\r
6809 \r
6810   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6811 \r
6812   if (str == NULL) str = "";\r
6813   p = (char *) malloc(2 * strlen(str) + 2);\r
6814   q = p;\r
6815   while (*str) {\r
6816     if (*str == '\n') *q++ = '\r';\r
6817     *q++ = *str++;\r
6818   }\r
6819   *q = NULLCHAR;\r
6820   if (commentText != NULL) free(commentText);\r
6821 \r
6822   commentIndex = index;\r
6823   commentTitle = title;\r
6824   commentText = p;\r
6825   editComment = edit;\r
6826 \r
6827   if (commentDialog) {\r
6828     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6829     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6830   } else {\r
6831     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6832     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6833                  hwndMain, (DLGPROC)lpProc);\r
6834     FreeProcInstance(lpProc);\r
6835   }\r
6836   commentDialogUp = TRUE;\r
6837 }\r
6838 \r
6839 \r
6840 /*---------------------------------------------------------------------------*\\r
6841  *\r
6842  * Type-in move dialog functions\r
6843  * \r
6844 \*---------------------------------------------------------------------------*/\r
6845 \r
6846 LRESULT CALLBACK\r
6847 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6848 {\r
6849   char move[MSG_SIZ];\r
6850   HWND hInput;\r
6851   ChessMove moveType;\r
6852   int fromX, fromY, toX, toY;\r
6853   char promoChar;\r
6854 \r
6855   switch (message) {\r
6856   case WM_INITDIALOG:\r
6857     move[0] = (char) lParam;\r
6858     move[1] = NULLCHAR;\r
6859     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6860     hInput = GetDlgItem(hDlg, OPT_Move);\r
6861     SetWindowText(hInput, move);\r
6862     SetFocus(hInput);\r
6863     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6864     return FALSE;\r
6865 \r
6866   case WM_COMMAND:\r
6867     switch (LOWORD(wParam)) {\r
6868     case IDOK:\r
6869       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6870         gameMode != Training) {\r
6871         DisplayMoveError("Displayed move is not current");\r
6872       } else {\r
6873         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6874         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6875           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6876           if (gameMode != Training)\r
6877               forwardMostMove = currentMove;\r
6878           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6879         } else {\r
6880           DisplayMoveError("Could not parse move");\r
6881         }\r
6882       }\r
6883       EndDialog(hDlg, TRUE);\r
6884       return TRUE;\r
6885     case IDCANCEL:\r
6886       EndDialog(hDlg, FALSE);\r
6887       return TRUE;\r
6888     default:\r
6889       break;\r
6890     }\r
6891     break;\r
6892   }\r
6893   return FALSE;\r
6894 }\r
6895 \r
6896 VOID\r
6897 PopUpMoveDialog(char firstchar)\r
6898 {\r
6899     FARPROC lpProc;\r
6900     \r
6901     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6902         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6903         gameMode == AnalyzeMode || gameMode == EditGame || \r
6904         gameMode == EditPosition || gameMode == IcsExamining ||\r
6905         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6906         gameMode == Training) {\r
6907       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6908       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6909         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6910       FreeProcInstance(lpProc);\r
6911     }\r
6912 }\r
6913 \r
6914 /*---------------------------------------------------------------------------*\\r
6915  *\r
6916  * Type-in name dialog functions\r
6917  * \r
6918 \*---------------------------------------------------------------------------*/\r
6919 \r
6920 LRESULT CALLBACK\r
6921 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6922 {\r
6923   char move[MSG_SIZ];\r
6924   HWND hInput;\r
6925 \r
6926   switch (message) {\r
6927   case WM_INITDIALOG:\r
6928     move[0] = (char) lParam;\r
6929     move[1] = NULLCHAR;\r
6930     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6931     hInput = GetDlgItem(hDlg, OPT_Name);\r
6932     SetWindowText(hInput, move);\r
6933     SetFocus(hInput);\r
6934     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6935     return FALSE;\r
6936 \r
6937   case WM_COMMAND:\r
6938     switch (LOWORD(wParam)) {\r
6939     case IDOK:\r
6940       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6941       appData.userName = strdup(move);\r
6942 \r
6943       EndDialog(hDlg, TRUE);\r
6944       return TRUE;\r
6945     case IDCANCEL:\r
6946       EndDialog(hDlg, FALSE);\r
6947       return TRUE;\r
6948     default:\r
6949       break;\r
6950     }\r
6951     break;\r
6952   }\r
6953   return FALSE;\r
6954 }\r
6955 \r
6956 VOID\r
6957 PopUpNameDialog(char firstchar)\r
6958 {\r
6959     FARPROC lpProc;\r
6960     \r
6961       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6962       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6963         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6964       FreeProcInstance(lpProc);\r
6965 }\r
6966 \r
6967 /*---------------------------------------------------------------------------*\\r
6968  *\r
6969  *  Error dialogs\r
6970  * \r
6971 \*---------------------------------------------------------------------------*/\r
6972 \r
6973 /* Nonmodal error box */\r
6974 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6975                              WPARAM wParam, LPARAM lParam);\r
6976 \r
6977 VOID\r
6978 ErrorPopUp(char *title, char *content)\r
6979 {\r
6980   FARPROC lpProc;\r
6981   char *p, *q;\r
6982   BOOLEAN modal = hwndMain == NULL;\r
6983 \r
6984   p = content;\r
6985   q = errorMessage;\r
6986   while (*p) {\r
6987     if (*p == '\n') {\r
6988       if (modal) {\r
6989         *q++ = ' ';\r
6990         p++;\r
6991       } else {\r
6992         *q++ = '\r';\r
6993         *q++ = *p++;\r
6994       }\r
6995     } else {\r
6996       *q++ = *p++;\r
6997     }\r
6998   }\r
6999   *q = NULLCHAR;\r
7000   strncpy(errorTitle, title, sizeof(errorTitle));\r
7001   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7002   \r
7003   if (modal) {\r
7004     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7005   } else {\r
7006     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7007     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7008                  hwndMain, (DLGPROC)lpProc);\r
7009     FreeProcInstance(lpProc);\r
7010   }\r
7011 }\r
7012 \r
7013 VOID\r
7014 ErrorPopDown()\r
7015 {\r
7016   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7017   if (errorDialog == NULL) return;\r
7018   DestroyWindow(errorDialog);\r
7019   errorDialog = NULL;\r
7020 }\r
7021 \r
7022 LRESULT CALLBACK\r
7023 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7024 {\r
7025   HANDLE hwndText;\r
7026   RECT rChild;\r
7027 \r
7028   switch (message) {\r
7029   case WM_INITDIALOG:\r
7030     GetWindowRect(hDlg, &rChild);\r
7031 \r
7032     /*\r
7033     SetWindowPos(hDlg, NULL, rChild.left,\r
7034       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7035       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7036     */\r
7037 \r
7038     /* \r
7039         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7040         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7041         and it doesn't work when you resize the dialog.\r
7042         For now, just give it a default position.\r
7043     */\r
7044     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7045 \r
7046     errorDialog = hDlg;\r
7047     SetWindowText(hDlg, errorTitle);\r
7048     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7049     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7050     return FALSE;\r
7051 \r
7052   case WM_COMMAND:\r
7053     switch (LOWORD(wParam)) {\r
7054     case IDOK:\r
7055     case IDCANCEL:\r
7056       if (errorDialog == hDlg) errorDialog = NULL;\r
7057       DestroyWindow(hDlg);\r
7058       return TRUE;\r
7059 \r
7060     default:\r
7061       break;\r
7062     }\r
7063     break;\r
7064   }\r
7065   return FALSE;\r
7066 }\r
7067 \r
7068 #ifdef GOTHIC\r
7069 HWND gothicDialog = NULL;\r
7070 \r
7071 LRESULT CALLBACK\r
7072 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7073 {\r
7074   HANDLE hwndText;\r
7075   RECT rChild;\r
7076   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7077 \r
7078   switch (message) {\r
7079   case WM_INITDIALOG:\r
7080     GetWindowRect(hDlg, &rChild);\r
7081 \r
7082     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7083                                                              SWP_NOZORDER);\r
7084 \r
7085     /* \r
7086         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7087         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7088         and it doesn't work when you resize the dialog.\r
7089         For now, just give it a default position.\r
7090     */\r
7091     gothicDialog = hDlg;\r
7092     SetWindowText(hDlg, errorTitle);\r
7093     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7094     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7095     return FALSE;\r
7096 \r
7097   case WM_COMMAND:\r
7098     switch (LOWORD(wParam)) {\r
7099     case IDOK:\r
7100     case IDCANCEL:\r
7101       if (errorDialog == hDlg) errorDialog = NULL;\r
7102       DestroyWindow(hDlg);\r
7103       return TRUE;\r
7104 \r
7105     default:\r
7106       break;\r
7107     }\r
7108     break;\r
7109   }\r
7110   return FALSE;\r
7111 }\r
7112 \r
7113 VOID\r
7114 GothicPopUp(char *title, VariantClass variant)\r
7115 {\r
7116   FARPROC lpProc;\r
7117   char *p, *q;\r
7118   BOOLEAN modal = hwndMain == NULL;\r
7119   static char *lastTitle;\r
7120 \r
7121   strncpy(errorTitle, title, sizeof(errorTitle));\r
7122   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7123 \r
7124   if(lastTitle != title && gothicDialog != NULL) {\r
7125     DestroyWindow(gothicDialog);\r
7126     gothicDialog = NULL;\r
7127   }\r
7128   if(variant != VariantNormal && gothicDialog == NULL) {\r
7129     title = lastTitle;\r
7130     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7131     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7132                  hwndMain, (DLGPROC)lpProc);\r
7133     FreeProcInstance(lpProc);\r
7134   }\r
7135 }\r
7136 #endif\r
7137 \r
7138 /*---------------------------------------------------------------------------*\\r
7139  *\r
7140  *  Ics Interaction console functions\r
7141  *\r
7142 \*---------------------------------------------------------------------------*/\r
7143 \r
7144 #define HISTORY_SIZE 64\r
7145 static char *history[HISTORY_SIZE];\r
7146 int histIn = 0, histP = 0;\r
7147 \r
7148 VOID\r
7149 SaveInHistory(char *cmd)\r
7150 {\r
7151   if (history[histIn] != NULL) {\r
7152     free(history[histIn]);\r
7153     history[histIn] = NULL;\r
7154   }\r
7155   if (*cmd == NULLCHAR) return;\r
7156   history[histIn] = StrSave(cmd);\r
7157   histIn = (histIn + 1) % HISTORY_SIZE;\r
7158   if (history[histIn] != NULL) {\r
7159     free(history[histIn]);\r
7160     history[histIn] = NULL;\r
7161   }\r
7162   histP = histIn;\r
7163 }\r
7164 \r
7165 char *\r
7166 PrevInHistory(char *cmd)\r
7167 {\r
7168   int newhp;\r
7169   if (histP == histIn) {\r
7170     if (history[histIn] != NULL) free(history[histIn]);\r
7171     history[histIn] = StrSave(cmd);\r
7172   }\r
7173   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7174   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7175   histP = newhp;\r
7176   return history[histP];\r
7177 }\r
7178 \r
7179 char *\r
7180 NextInHistory()\r
7181 {\r
7182   if (histP == histIn) return NULL;\r
7183   histP = (histP + 1) % HISTORY_SIZE;\r
7184   return history[histP];\r
7185 }\r
7186 \r
7187 typedef struct {\r
7188   char *item;\r
7189   char *command;\r
7190   BOOLEAN getname;\r
7191   BOOLEAN immediate;\r
7192 } IcsTextMenuEntry;\r
7193 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7194 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7195 \r
7196 void\r
7197 ParseIcsTextMenu(char *icsTextMenuString)\r
7198 {\r
7199   int flags = 0;\r
7200   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7201   char *p = icsTextMenuString;\r
7202   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7203     free(e->item);\r
7204     e->item = NULL;\r
7205     if (e->command != NULL) {\r
7206       free(e->command);\r
7207       e->command = NULL;\r
7208     }\r
7209     e++;\r
7210   }\r
7211   e = icsTextMenuEntry;\r
7212   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7213     if (*p == ';' || *p == '\n') {\r
7214       e->item = strdup("-");\r
7215       e->command = NULL;\r
7216       p++;\r
7217     } else if (*p == '-') {\r
7218       e->item = strdup("-");\r
7219       e->command = NULL;\r
7220       p++;\r
7221       if (*p) p++;\r
7222     } else {\r
7223       char *q, *r, *s, *t;\r
7224       char c;\r
7225       q = strchr(p, ',');\r
7226       if (q == NULL) break;\r
7227       *q = NULLCHAR;\r
7228       r = strchr(q + 1, ',');\r
7229       if (r == NULL) break;\r
7230       *r = NULLCHAR;\r
7231       s = strchr(r + 1, ',');\r
7232       if (s == NULL) break;\r
7233       *s = NULLCHAR;\r
7234       c = ';';\r
7235       t = strchr(s + 1, c);\r
7236       if (t == NULL) {\r
7237         c = '\n';\r
7238         t = strchr(s + 1, c);\r
7239       }\r
7240       if (t != NULL) *t = NULLCHAR;\r
7241       e->item = strdup(p);\r
7242       e->command = strdup(q + 1);\r
7243       e->getname = *(r + 1) != '0';\r
7244       e->immediate = *(s + 1) != '0';\r
7245       *q = ',';\r
7246       *r = ',';\r
7247       *s = ',';\r
7248       if (t == NULL) break;\r
7249       *t = c;\r
7250       p = t + 1;\r
7251     }\r
7252     e++;\r
7253   } \r
7254 }\r
7255 \r
7256 HMENU\r
7257 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7258 {\r
7259   HMENU hmenu, h;\r
7260   int i = 0;\r
7261   hmenu = LoadMenu(hInst, "TextMenu");\r
7262   h = GetSubMenu(hmenu, 0);\r
7263   while (e->item) {\r
7264     if (strcmp(e->item, "-") == 0) {\r
7265       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7266     } else {\r
7267       if (e->item[0] == '|') {\r
7268         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7269                    IDM_CommandX + i, &e->item[1]);\r
7270       } else {\r
7271         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7272       }\r
7273     }\r
7274     e++;\r
7275     i++;\r
7276   } \r
7277   return hmenu;\r
7278 }\r
7279 \r
7280 WNDPROC consoleTextWindowProc;\r
7281 \r
7282 void\r
7283 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7284 {\r
7285   char buf[MSG_SIZ], name[MSG_SIZ];\r
7286   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7287   CHARRANGE sel;\r
7288 \r
7289   if (!getname) {\r
7290     SetWindowText(hInput, command);\r
7291     if (immediate) {\r
7292       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7293     } else {\r
7294       sel.cpMin = 999999;\r
7295       sel.cpMax = 999999;\r
7296       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7297       SetFocus(hInput);\r
7298     }\r
7299     return;\r
7300   }    \r
7301   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7302   if (sel.cpMin == sel.cpMax) {\r
7303     /* Expand to surrounding word */\r
7304     TEXTRANGE tr;\r
7305     do {\r
7306       tr.chrg.cpMax = sel.cpMin;\r
7307       tr.chrg.cpMin = --sel.cpMin;\r
7308       if (sel.cpMin < 0) break;\r
7309       tr.lpstrText = name;\r
7310       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7311     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7312     sel.cpMin++;\r
7313 \r
7314     do {\r
7315       tr.chrg.cpMin = sel.cpMax;\r
7316       tr.chrg.cpMax = ++sel.cpMax;\r
7317       tr.lpstrText = name;\r
7318       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7319     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7320     sel.cpMax--;\r
7321 \r
7322     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7323       MessageBeep(MB_ICONEXCLAMATION);\r
7324       return;\r
7325     }\r
7326     tr.chrg = sel;\r
7327     tr.lpstrText = name;\r
7328     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7329   } else {\r
7330     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7331       MessageBeep(MB_ICONEXCLAMATION);\r
7332       return;\r
7333     }\r
7334     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7335   }\r
7336   if (immediate) {\r
7337     sprintf(buf, "%s %s", command, name);\r
7338     SetWindowText(hInput, buf);\r
7339     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7340   } else {\r
7341     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7342     SetWindowText(hInput, buf);\r
7343     sel.cpMin = 999999;\r
7344     sel.cpMax = 999999;\r
7345     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7346     SetFocus(hInput);\r
7347   }\r
7348 }\r
7349 \r
7350 LRESULT CALLBACK \r
7351 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7352 {\r
7353   HWND hInput;\r
7354   CHARRANGE sel;\r
7355 \r
7356   switch (message) {\r
7357   case WM_KEYDOWN:\r
7358     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7359     switch (wParam) {\r
7360     case VK_PRIOR:\r
7361       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7362       return 0;\r
7363     case VK_NEXT:\r
7364       sel.cpMin = 999999;\r
7365       sel.cpMax = 999999;\r
7366       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7367       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7368       return 0;\r
7369     }\r
7370     break;\r
7371   case WM_CHAR:\r
7372     if (wParam == '\t') {\r
7373       if (GetKeyState(VK_SHIFT) < 0) {\r
7374         /* shifted */\r
7375         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7376         if (buttonDesc[0].hwnd) {\r
7377           SetFocus(buttonDesc[0].hwnd);\r
7378         } else {\r
7379           SetFocus(hwndMain);\r
7380         }\r
7381       } else {\r
7382         /* unshifted */\r
7383         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7384       }\r
7385     } else {\r
7386       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7387       SetFocus(hInput);\r
7388       SendMessage(hInput, message, wParam, lParam);\r
7389     }\r
7390     return 0;\r
7391   case WM_PASTE:\r
7392     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7393     SetFocus(hInput);\r
7394     return SendMessage(hInput, message, wParam, lParam);\r
7395   case WM_MBUTTONDOWN:\r
7396     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7397   case WM_RBUTTONDOWN:\r
7398     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7399       /* Move selection here if it was empty */\r
7400       POINT pt;\r
7401       pt.x = LOWORD(lParam);\r
7402       pt.y = HIWORD(lParam);\r
7403       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7404       if (sel.cpMin == sel.cpMax) {\r
7405         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7406         sel.cpMax = sel.cpMin;\r
7407         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7408       }\r
7409       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7410     }\r
7411     return 0;\r
7412   case WM_RBUTTONUP:\r
7413     if (GetKeyState(VK_SHIFT) & ~1) {\r
7414       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7415         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7416     } else {\r
7417       POINT pt;\r
7418       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7419       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7420       if (sel.cpMin == sel.cpMax) {\r
7421         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7422         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7423       }\r
7424       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7425         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7426       }\r
7427       pt.x = LOWORD(lParam);\r
7428       pt.y = HIWORD(lParam);\r
7429       MenuPopup(hwnd, pt, hmenu, -1);\r
7430     }\r
7431     return 0;\r
7432   case WM_COMMAND:\r
7433     switch (LOWORD(wParam)) {\r
7434     case IDM_QuickPaste:\r
7435       {\r
7436         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7437         if (sel.cpMin == sel.cpMax) {\r
7438           MessageBeep(MB_ICONEXCLAMATION);\r
7439           return 0;\r
7440         }\r
7441         SendMessage(hwnd, WM_COPY, 0, 0);\r
7442         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7443         SendMessage(hInput, WM_PASTE, 0, 0);\r
7444         SetFocus(hInput);\r
7445         return 0;\r
7446       }\r
7447     case IDM_Cut:\r
7448       SendMessage(hwnd, WM_CUT, 0, 0);\r
7449       return 0;\r
7450     case IDM_Paste:\r
7451       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7452       return 0;\r
7453     case IDM_Copy:\r
7454       SendMessage(hwnd, WM_COPY, 0, 0);\r
7455       return 0;\r
7456     default:\r
7457       {\r
7458         int i = LOWORD(wParam) - IDM_CommandX;\r
7459         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7460             icsTextMenuEntry[i].command != NULL) {\r
7461           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7462                    icsTextMenuEntry[i].getname,\r
7463                    icsTextMenuEntry[i].immediate);\r
7464           return 0;\r
7465         }\r
7466       }\r
7467       break;\r
7468     }\r
7469     break;\r
7470   }\r
7471   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7472 }\r
7473 \r
7474 WNDPROC consoleInputWindowProc;\r
7475 \r
7476 LRESULT CALLBACK\r
7477 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7478 {\r
7479   char buf[MSG_SIZ];\r
7480   char *p;\r
7481   static BOOL sendNextChar = FALSE;\r
7482   static BOOL quoteNextChar = FALSE;\r
7483   InputSource *is = consoleInputSource;\r
7484   CHARFORMAT cf;\r
7485   CHARRANGE sel;\r
7486 \r
7487   switch (message) {\r
7488   case WM_CHAR:\r
7489     if (!appData.localLineEditing || sendNextChar) {\r
7490       is->buf[0] = (CHAR) wParam;\r
7491       is->count = 1;\r
7492       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7493       sendNextChar = FALSE;\r
7494       return 0;\r
7495     }\r
7496     if (quoteNextChar) {\r
7497       buf[0] = (char) wParam;\r
7498       buf[1] = NULLCHAR;\r
7499       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7500       quoteNextChar = FALSE;\r
7501       return 0;\r
7502     }\r
7503     switch (wParam) {\r
7504     case '\r':   /* Enter key */\r
7505       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7506       if (consoleEcho) SaveInHistory(is->buf);\r
7507       is->buf[is->count++] = '\n';\r
7508       is->buf[is->count] = NULLCHAR;\r
7509       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7510       if (consoleEcho) {\r
7511         ConsoleOutput(is->buf, is->count, TRUE);\r
7512       } else if (appData.localLineEditing) {\r
7513         ConsoleOutput("\n", 1, TRUE);\r
7514       }\r
7515       /* fall thru */\r
7516     case '\033': /* Escape key */\r
7517       SetWindowText(hwnd, "");\r
7518       cf.cbSize = sizeof(CHARFORMAT);\r
7519       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7520       if (consoleEcho) {\r
7521         cf.crTextColor = textAttribs[ColorNormal].color;\r
7522       } else {\r
7523         cf.crTextColor = COLOR_ECHOOFF;\r
7524       }\r
7525       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7526       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7527       return 0;\r
7528     case '\t':   /* Tab key */\r
7529       if (GetKeyState(VK_SHIFT) < 0) {\r
7530         /* shifted */\r
7531         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7532       } else {\r
7533         /* unshifted */\r
7534         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7535         if (buttonDesc[0].hwnd) {\r
7536           SetFocus(buttonDesc[0].hwnd);\r
7537         } else {\r
7538           SetFocus(hwndMain);\r
7539         }\r
7540       }\r
7541       return 0;\r
7542     case '\023': /* Ctrl+S */\r
7543       sendNextChar = TRUE;\r
7544       return 0;\r
7545     case '\021': /* Ctrl+Q */\r
7546       quoteNextChar = TRUE;\r
7547       return 0;\r
7548     default:\r
7549       break;\r
7550     }\r
7551     break;\r
7552   case WM_KEYDOWN:\r
7553     switch (wParam) {\r
7554     case VK_UP:\r
7555       GetWindowText(hwnd, buf, MSG_SIZ);\r
7556       p = PrevInHistory(buf);\r
7557       if (p != NULL) {\r
7558         SetWindowText(hwnd, p);\r
7559         sel.cpMin = 999999;\r
7560         sel.cpMax = 999999;\r
7561         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7562         return 0;\r
7563       }\r
7564       break;\r
7565     case VK_DOWN:\r
7566       p = NextInHistory();\r
7567       if (p != NULL) {\r
7568         SetWindowText(hwnd, p);\r
7569         sel.cpMin = 999999;\r
7570         sel.cpMax = 999999;\r
7571         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7572         return 0;\r
7573       }\r
7574       break;\r
7575     case VK_HOME:\r
7576     case VK_END:\r
7577       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7578       /* fall thru */\r
7579     case VK_PRIOR:\r
7580     case VK_NEXT:\r
7581       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7582       return 0;\r
7583     }\r
7584     break;\r
7585   case WM_MBUTTONDOWN:\r
7586     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7587       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7588     break;\r
7589   case WM_RBUTTONUP:\r
7590     if (GetKeyState(VK_SHIFT) & ~1) {\r
7591       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7592         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7593     } else {\r
7594       POINT pt;\r
7595       HMENU hmenu;\r
7596       hmenu = LoadMenu(hInst, "InputMenu");\r
7597       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7598       if (sel.cpMin == sel.cpMax) {\r
7599         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7600         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7601       }\r
7602       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7603         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7604       }\r
7605       pt.x = LOWORD(lParam);\r
7606       pt.y = HIWORD(lParam);\r
7607       MenuPopup(hwnd, pt, hmenu, -1);\r
7608     }\r
7609     return 0;\r
7610   case WM_COMMAND:\r
7611     switch (LOWORD(wParam)) { \r
7612     case IDM_Undo:\r
7613       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7614       return 0;\r
7615     case IDM_SelectAll:\r
7616       sel.cpMin = 0;\r
7617       sel.cpMax = -1; /*999999?*/\r
7618       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7619       return 0;\r
7620     case IDM_Cut:\r
7621       SendMessage(hwnd, WM_CUT, 0, 0);\r
7622       return 0;\r
7623     case IDM_Paste:\r
7624       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7625       return 0;\r
7626     case IDM_Copy:\r
7627       SendMessage(hwnd, WM_COPY, 0, 0);\r
7628       return 0;\r
7629     }\r
7630     break;\r
7631   }\r
7632   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7633 }\r
7634 \r
7635 #define CO_MAX  100000\r
7636 #define CO_TRIM   1000\r
7637 \r
7638 LRESULT CALLBACK\r
7639 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7640 {\r
7641   static SnapData sd;\r
7642   static HWND hText, hInput, hFocus;\r
7643   InputSource *is = consoleInputSource;\r
7644   RECT rect;\r
7645   static int sizeX, sizeY;\r
7646   int newSizeX, newSizeY;\r
7647   MINMAXINFO *mmi;\r
7648 \r
7649   switch (message) {\r
7650   case WM_INITDIALOG: /* message: initialize dialog box */\r
7651     hwndConsole = hDlg;\r
7652     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7653     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7654     SetFocus(hInput);\r
7655     consoleTextWindowProc = (WNDPROC)\r
7656       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7657     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7658     consoleInputWindowProc = (WNDPROC)\r
7659       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7660     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7661     Colorize(ColorNormal, TRUE);\r
7662     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7663     ChangedConsoleFont();\r
7664     GetClientRect(hDlg, &rect);\r
7665     sizeX = rect.right;\r
7666     sizeY = rect.bottom;\r
7667     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7668         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7669       WINDOWPLACEMENT wp;\r
7670       EnsureOnScreen(&consoleX, &consoleY);\r
7671       wp.length = sizeof(WINDOWPLACEMENT);\r
7672       wp.flags = 0;\r
7673       wp.showCmd = SW_SHOW;\r
7674       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7675       wp.rcNormalPosition.left = consoleX;\r
7676       wp.rcNormalPosition.right = consoleX + consoleW;\r
7677       wp.rcNormalPosition.top = consoleY;\r
7678       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7679       SetWindowPlacement(hDlg, &wp);\r
7680     }\r
7681     return FALSE;\r
7682 \r
7683   case WM_SETFOCUS:\r
7684     SetFocus(hInput);\r
7685     return 0;\r
7686 \r
7687   case WM_CLOSE:\r
7688     ExitEvent(0);\r
7689     /* not reached */\r
7690     break;\r
7691 \r
7692   case WM_SIZE:\r
7693     if (IsIconic(hDlg)) break;\r
7694     newSizeX = LOWORD(lParam);\r
7695     newSizeY = HIWORD(lParam);\r
7696     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7697       RECT rectText, rectInput;\r
7698       POINT pt;\r
7699       int newTextHeight, newTextWidth;\r
7700       GetWindowRect(hText, &rectText);\r
7701       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7702       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7703       if (newTextHeight < 0) {\r
7704         newSizeY += -newTextHeight;\r
7705         newTextHeight = 0;\r
7706       }\r
7707       SetWindowPos(hText, NULL, 0, 0,\r
7708         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7709       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7710       pt.x = rectInput.left;\r
7711       pt.y = rectInput.top + newSizeY - sizeY;\r
7712       ScreenToClient(hDlg, &pt);\r
7713       SetWindowPos(hInput, NULL, \r
7714         pt.x, pt.y, /* needs client coords */   \r
7715         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7716         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7717     }\r
7718     sizeX = newSizeX;\r
7719     sizeY = newSizeY;\r
7720     break;\r
7721 \r
7722   case WM_GETMINMAXINFO:\r
7723     /* Prevent resizing window too small */\r
7724     mmi = (MINMAXINFO *) lParam;\r
7725     mmi->ptMinTrackSize.x = 100;\r
7726     mmi->ptMinTrackSize.y = 100;\r
7727     break;\r
7728 \r
7729   /* [AS] Snapping */\r
7730   case WM_ENTERSIZEMOVE:\r
7731     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7732 \r
7733   case WM_SIZING:\r
7734     return OnSizing( &sd, hDlg, wParam, lParam );\r
7735 \r
7736   case WM_MOVING:\r
7737     return OnMoving( &sd, hDlg, wParam, lParam );\r
7738 \r
7739   case WM_EXITSIZEMOVE:\r
7740     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7741   }\r
7742 \r
7743   return DefWindowProc(hDlg, message, wParam, lParam);\r
7744 }\r
7745 \r
7746 \r
7747 VOID\r
7748 ConsoleCreate()\r
7749 {\r
7750   HWND hCons;\r
7751   if (hwndConsole) return;\r
7752   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7753   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7754 }\r
7755 \r
7756 \r
7757 VOID\r
7758 ConsoleOutput(char* data, int length, int forceVisible)\r
7759 {\r
7760   HWND hText;\r
7761   int trim, exlen;\r
7762   char *p, *q;\r
7763   char buf[CO_MAX+1];\r
7764   POINT pEnd;\r
7765   RECT rect;\r
7766   static int delayLF = 0;\r
7767   CHARRANGE savesel, sel;\r
7768 \r
7769   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7770   p = data;\r
7771   q = buf;\r
7772   if (delayLF) {\r
7773     *q++ = '\r';\r
7774     *q++ = '\n';\r
7775     delayLF = 0;\r
7776   }\r
7777   while (length--) {\r
7778     if (*p == '\n') {\r
7779       if (*++p) {\r
7780         *q++ = '\r';\r
7781         *q++ = '\n';\r
7782       } else {\r
7783         delayLF = 1;\r
7784       }\r
7785     } else if (*p == '\007') {\r
7786        MyPlaySound(&sounds[(int)SoundBell]);\r
7787        p++;\r
7788     } else {\r
7789       *q++ = *p++;\r
7790     }\r
7791   }\r
7792   *q = NULLCHAR;\r
7793   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7794   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7795   /* Save current selection */\r
7796   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7797   exlen = GetWindowTextLength(hText);\r
7798   /* Find out whether current end of text is visible */\r
7799   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7800   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7801   /* Trim existing text if it's too long */\r
7802   if (exlen + (q - buf) > CO_MAX) {\r
7803     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7804     sel.cpMin = 0;\r
7805     sel.cpMax = trim;\r
7806     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7807     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7808     exlen -= trim;\r
7809     savesel.cpMin -= trim;\r
7810     savesel.cpMax -= trim;\r
7811     if (exlen < 0) exlen = 0;\r
7812     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7813     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7814   }\r
7815   /* Append the new text */\r
7816   sel.cpMin = exlen;\r
7817   sel.cpMax = exlen;\r
7818   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7819   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7820   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7821   if (forceVisible || exlen == 0 ||\r
7822       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7823        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7824     /* Scroll to make new end of text visible if old end of text\r
7825        was visible or new text is an echo of user typein */\r
7826     sel.cpMin = 9999999;\r
7827     sel.cpMax = 9999999;\r
7828     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7829     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7830     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7831     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7832   }\r
7833   if (savesel.cpMax == exlen || forceVisible) {\r
7834     /* Move insert point to new end of text if it was at the old\r
7835        end of text or if the new text is an echo of user typein */\r
7836     sel.cpMin = 9999999;\r
7837     sel.cpMax = 9999999;\r
7838     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7839   } else {\r
7840     /* Restore previous selection */\r
7841     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7842   }\r
7843   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7844 }\r
7845 \r
7846 /*---------*/\r
7847 \r
7848 \r
7849 void\r
7850 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7851 {\r
7852   char buf[100];\r
7853   char *str;\r
7854   COLORREF oldFg, oldBg;\r
7855   HFONT oldFont;\r
7856   RECT rect;\r
7857 \r
7858   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7859 \r
7860   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7861   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7862   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7863 \r
7864   rect.left = x;\r
7865   rect.right = x + squareSize;\r
7866   rect.top  = y;\r
7867   rect.bottom = y + squareSize;\r
7868   str = buf;\r
7869 \r
7870   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7871                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7872              y, ETO_CLIPPED|ETO_OPAQUE,\r
7873              &rect, str, strlen(str), NULL);\r
7874 \r
7875   (void) SetTextColor(hdc, oldFg);\r
7876   (void) SetBkColor(hdc, oldBg);\r
7877   (void) SelectObject(hdc, oldFont);\r
7878 }\r
7879 \r
7880 void\r
7881 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7882               RECT *rect, char *color, char *flagFell)\r
7883 {\r
7884   char buf[100];\r
7885   char *str;\r
7886   COLORREF oldFg, oldBg;\r
7887   HFONT oldFont;\r
7888 \r
7889   if (appData.clockMode) {\r
7890     if (tinyLayout)\r
7891       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7892     else\r
7893       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7894     str = buf;\r
7895   } else {\r
7896     str = color;\r
7897   }\r
7898 \r
7899   if (highlight) {\r
7900     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7901     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7902   } else {\r
7903     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7904     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7905   }\r
7906   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7907 \r
7908   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7909              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7910              rect, str, strlen(str), NULL);\r
7911 \r
7912   (void) SetTextColor(hdc, oldFg);\r
7913   (void) SetBkColor(hdc, oldBg);\r
7914   (void) SelectObject(hdc, oldFont);\r
7915 }\r
7916 \r
7917 \r
7918 int\r
7919 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7920            OVERLAPPED *ovl)\r
7921 {\r
7922   int ok, err;\r
7923 \r
7924   /* [AS]  */\r
7925   if( count <= 0 ) {\r
7926     if (appData.debugMode) {\r
7927       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7928     }\r
7929 \r
7930     return ERROR_INVALID_USER_BUFFER;\r
7931   }\r
7932 \r
7933   ResetEvent(ovl->hEvent);\r
7934   ovl->Offset = ovl->OffsetHigh = 0;\r
7935   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7936   if (ok) {\r
7937     err = NO_ERROR;\r
7938   } else {\r
7939     err = GetLastError();\r
7940     if (err == ERROR_IO_PENDING) {\r
7941       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7942       if (ok)\r
7943         err = NO_ERROR;\r
7944       else\r
7945         err = GetLastError();\r
7946     }\r
7947   }\r
7948   return err;\r
7949 }\r
7950 \r
7951 int\r
7952 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7953             OVERLAPPED *ovl)\r
7954 {\r
7955   int ok, err;\r
7956 \r
7957   ResetEvent(ovl->hEvent);\r
7958   ovl->Offset = ovl->OffsetHigh = 0;\r
7959   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7960   if (ok) {\r
7961     err = NO_ERROR;\r
7962   } else {\r
7963     err = GetLastError();\r
7964     if (err == ERROR_IO_PENDING) {\r
7965       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7966       if (ok)\r
7967         err = NO_ERROR;\r
7968       else\r
7969         err = GetLastError();\r
7970     }\r
7971   }\r
7972   return err;\r
7973 }\r
7974 \r
7975 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7976 void CheckForInputBufferFull( InputSource * is )\r
7977 {\r
7978     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7979         /* Look for end of line */\r
7980         char * p = is->buf;\r
7981         \r
7982         while( p < is->next && *p != '\n' ) {\r
7983             p++;\r
7984         }\r
7985 \r
7986         if( p >= is->next ) {\r
7987             if (appData.debugMode) {\r
7988                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7989             }\r
7990 \r
7991             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7992             is->count = (DWORD) -1;\r
7993             is->next = is->buf;\r
7994         }\r
7995     }\r
7996 }\r
7997 \r
7998 DWORD\r
7999 InputThread(LPVOID arg)\r
8000 {\r
8001   InputSource *is;\r
8002   OVERLAPPED ovl;\r
8003 \r
8004   is = (InputSource *) arg;\r
8005   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8006   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8007   while (is->hThread != NULL) {\r
8008     is->error = DoReadFile(is->hFile, is->next,\r
8009                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8010                            &is->count, &ovl);\r
8011     if (is->error == NO_ERROR) {\r
8012       is->next += is->count;\r
8013     } else {\r
8014       if (is->error == ERROR_BROKEN_PIPE) {\r
8015         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8016         is->count = 0;\r
8017       } else {\r
8018         is->count = (DWORD) -1;\r
8019         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8020         break; \r
8021       }\r
8022     }\r
8023 \r
8024     CheckForInputBufferFull( is );\r
8025 \r
8026     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8027 \r
8028     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8029 \r
8030     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8031   }\r
8032 \r
8033   CloseHandle(ovl.hEvent);\r
8034   CloseHandle(is->hFile);\r
8035 \r
8036   if (appData.debugMode) {\r
8037     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8038   }\r
8039 \r
8040   return 0;\r
8041 }\r
8042 \r
8043 \r
8044 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8045 DWORD\r
8046 NonOvlInputThread(LPVOID arg)\r
8047 {\r
8048   InputSource *is;\r
8049   char *p, *q;\r
8050   int i;\r
8051   char prev;\r
8052 \r
8053   is = (InputSource *) arg;\r
8054   while (is->hThread != NULL) {\r
8055     is->error = ReadFile(is->hFile, is->next,\r
8056                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8057                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8058     if (is->error == NO_ERROR) {\r
8059       /* Change CRLF to LF */\r
8060       if (is->next > is->buf) {\r
8061         p = is->next - 1;\r
8062         i = is->count + 1;\r
8063       } else {\r
8064         p = is->next;\r
8065         i = is->count;\r
8066       }\r
8067       q = p;\r
8068       prev = NULLCHAR;\r
8069       while (i > 0) {\r
8070         if (prev == '\r' && *p == '\n') {\r
8071           *(q-1) = '\n';\r
8072           is->count--;\r
8073         } else { \r
8074           *q++ = *p;\r
8075         }\r
8076         prev = *p++;\r
8077         i--;\r
8078       }\r
8079       *q = NULLCHAR;\r
8080       is->next = q;\r
8081     } else {\r
8082       if (is->error == ERROR_BROKEN_PIPE) {\r
8083         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8084         is->count = 0; \r
8085       } else {\r
8086         is->count = (DWORD) -1;\r
8087       }\r
8088     }\r
8089 \r
8090     CheckForInputBufferFull( is );\r
8091 \r
8092     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8093 \r
8094     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8095 \r
8096     if (is->count < 0) break;  /* Quit on error */\r
8097   }\r
8098   CloseHandle(is->hFile);\r
8099   return 0;\r
8100 }\r
8101 \r
8102 DWORD\r
8103 SocketInputThread(LPVOID arg)\r
8104 {\r
8105   InputSource *is;\r
8106 \r
8107   is = (InputSource *) arg;\r
8108   while (is->hThread != NULL) {\r
8109     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8110     if ((int)is->count == SOCKET_ERROR) {\r
8111       is->count = (DWORD) -1;\r
8112       is->error = WSAGetLastError();\r
8113     } else {\r
8114       is->error = NO_ERROR;\r
8115       is->next += is->count;\r
8116       if (is->count == 0 && is->second == is) {\r
8117         /* End of file on stderr; quit with no message */\r
8118         break;\r
8119       }\r
8120     }\r
8121     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8122 \r
8123     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8124 \r
8125     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8126   }\r
8127   return 0;\r
8128 }\r
8129 \r
8130 VOID\r
8131 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8132 {\r
8133   InputSource *is;\r
8134 \r
8135   is = (InputSource *) lParam;\r
8136   if (is->lineByLine) {\r
8137     /* Feed in lines one by one */\r
8138     char *p = is->buf;\r
8139     char *q = p;\r
8140     while (q < is->next) {\r
8141       if (*q++ == '\n') {\r
8142         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8143         p = q;\r
8144       }\r
8145     }\r
8146     \r
8147     /* Move any partial line to the start of the buffer */\r
8148     q = is->buf;\r
8149     while (p < is->next) {\r
8150       *q++ = *p++;\r
8151     }\r
8152     is->next = q;\r
8153 \r
8154     if (is->error != NO_ERROR || is->count == 0) {\r
8155       /* Notify backend of the error.  Note: If there was a partial\r
8156          line at the end, it is not flushed through. */\r
8157       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8158     }\r
8159   } else {\r
8160     /* Feed in the whole chunk of input at once */\r
8161     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8162     is->next = is->buf;\r
8163   }\r
8164 }\r
8165 \r
8166 /*---------------------------------------------------------------------------*\\r
8167  *\r
8168  *  Menu enables. Used when setting various modes.\r
8169  *\r
8170 \*---------------------------------------------------------------------------*/\r
8171 \r
8172 typedef struct {\r
8173   int item;\r
8174   int flags;\r
8175 } Enables;\r
8176 \r
8177 VOID\r
8178 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8179 {\r
8180   while (enab->item > 0) {\r
8181     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8182     enab++;\r
8183   }\r
8184 }\r
8185 \r
8186 Enables gnuEnables[] = {\r
8187   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8188   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8189   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8190   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8191   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8192   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8193   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8194   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8195   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8196   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8197   { -1, -1 }\r
8198 };\r
8199 \r
8200 Enables icsEnables[] = {\r
8201   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8202   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8203   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8204   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8205   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8206   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8207   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8208   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8209   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8210   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8211   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8212   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8213   { -1, -1 }\r
8214 };\r
8215 \r
8216 #ifdef ZIPPY\r
8217 Enables zippyEnables[] = {\r
8218   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8219   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8220   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8221   { -1, -1 }\r
8222 };\r
8223 #endif\r
8224 \r
8225 Enables ncpEnables[] = {\r
8226   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8227   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8228   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8229   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8230   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8231   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8232   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8233   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8234   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8235   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8236   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8237   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8238   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8239   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8240   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8241   { -1, -1 }\r
8242 };\r
8243 \r
8244 Enables trainingOnEnables[] = {\r
8245   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8246   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8247   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8248   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8249   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8250   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8251   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8252   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8253   { -1, -1 }\r
8254 };\r
8255 \r
8256 Enables trainingOffEnables[] = {\r
8257   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8258   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8259   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8260   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8261   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8262   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8263   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8264   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8265   { -1, -1 }\r
8266 };\r
8267 \r
8268 /* These modify either ncpEnables or gnuEnables */\r
8269 Enables cmailEnables[] = {\r
8270   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8271   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8272   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8273   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8274   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8275   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8276   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8277   { -1, -1 }\r
8278 };\r
8279 \r
8280 Enables machineThinkingEnables[] = {\r
8281   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8282   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8283   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8284   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8285   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8286   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8287   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8288   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8289   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8290   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8291   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8292   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8293   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8294   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8295   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8296   { -1, -1 }\r
8297 };\r
8298 \r
8299 Enables userThinkingEnables[] = {\r
8300   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8301   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8302   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8303   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8304   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8305   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8306   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8307   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8308   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8309   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8310   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8311   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8312   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8313   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8314   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8315   { -1, -1 }\r
8316 };\r
8317 \r
8318 /*---------------------------------------------------------------------------*\\r
8319  *\r
8320  *  Front-end interface functions exported by XBoard.\r
8321  *  Functions appear in same order as prototypes in frontend.h.\r
8322  * \r
8323 \*---------------------------------------------------------------------------*/\r
8324 VOID\r
8325 ModeHighlight()\r
8326 {\r
8327   static UINT prevChecked = 0;\r
8328   static int prevPausing = 0;\r
8329   UINT nowChecked;\r
8330 \r
8331   if (pausing != prevPausing) {\r
8332     prevPausing = pausing;\r
8333     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8334                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8335     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8336   }\r
8337 \r
8338   switch (gameMode) {\r
8339   case BeginningOfGame:\r
8340     if (appData.icsActive)\r
8341       nowChecked = IDM_IcsClient;\r
8342     else if (appData.noChessProgram)\r
8343       nowChecked = IDM_EditGame;\r
8344     else\r
8345       nowChecked = IDM_MachineBlack;\r
8346     break;\r
8347   case MachinePlaysBlack:\r
8348     nowChecked = IDM_MachineBlack;\r
8349     break;\r
8350   case MachinePlaysWhite:\r
8351     nowChecked = IDM_MachineWhite;\r
8352     break;\r
8353   case TwoMachinesPlay:\r
8354     nowChecked = IDM_TwoMachines;\r
8355     break;\r
8356   case AnalyzeMode:\r
8357     nowChecked = IDM_AnalysisMode;\r
8358     break;\r
8359   case AnalyzeFile:\r
8360     nowChecked = IDM_AnalyzeFile;\r
8361     break;\r
8362   case EditGame:\r
8363     nowChecked = IDM_EditGame;\r
8364     break;\r
8365   case PlayFromGameFile:\r
8366     nowChecked = IDM_LoadGame;\r
8367     break;\r
8368   case EditPosition:\r
8369     nowChecked = IDM_EditPosition;\r
8370     break;\r
8371   case Training:\r
8372     nowChecked = IDM_Training;\r
8373     break;\r
8374   case IcsPlayingWhite:\r
8375   case IcsPlayingBlack:\r
8376   case IcsObserving:\r
8377   case IcsIdle:\r
8378     nowChecked = IDM_IcsClient;\r
8379     break;\r
8380   default:\r
8381   case EndOfGame:\r
8382     nowChecked = 0;\r
8383     break;\r
8384   }\r
8385   if (prevChecked != 0)\r
8386     (void) CheckMenuItem(GetMenu(hwndMain),\r
8387                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8388   if (nowChecked != 0)\r
8389     (void) CheckMenuItem(GetMenu(hwndMain),\r
8390                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8391 \r
8392   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8393     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8394                           MF_BYCOMMAND|MF_ENABLED);\r
8395   } else {\r
8396     (void) EnableMenuItem(GetMenu(hwndMain), \r
8397                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8398   }\r
8399 \r
8400   prevChecked = nowChecked;\r
8401 }\r
8402 \r
8403 VOID\r
8404 SetICSMode()\r
8405 {\r
8406   HMENU hmenu = GetMenu(hwndMain);\r
8407   SetMenuEnables(hmenu, icsEnables);\r
8408   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8409     MF_BYPOSITION|MF_ENABLED);\r
8410 #ifdef ZIPPY\r
8411   if (appData.zippyPlay) {\r
8412     SetMenuEnables(hmenu, zippyEnables);\r
8413   }\r
8414 #endif\r
8415 }\r
8416 \r
8417 VOID\r
8418 SetGNUMode()\r
8419 {\r
8420   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8421 }\r
8422 \r
8423 VOID\r
8424 SetNCPMode()\r
8425 {\r
8426   HMENU hmenu = GetMenu(hwndMain);\r
8427   SetMenuEnables(hmenu, ncpEnables);\r
8428   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8429     MF_BYPOSITION|MF_GRAYED);\r
8430     DrawMenuBar(hwndMain);\r
8431 }\r
8432 \r
8433 VOID\r
8434 SetCmailMode()\r
8435 {\r
8436   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8437 }\r
8438 \r
8439 VOID \r
8440 SetTrainingModeOn()\r
8441 {\r
8442   int i;\r
8443   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8444   for (i = 0; i < N_BUTTONS; i++) {\r
8445     if (buttonDesc[i].hwnd != NULL)\r
8446       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8447   }\r
8448   CommentPopDown();\r
8449 }\r
8450 \r
8451 VOID SetTrainingModeOff()\r
8452 {\r
8453   int i;\r
8454   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8455   for (i = 0; i < N_BUTTONS; i++) {\r
8456     if (buttonDesc[i].hwnd != NULL)\r
8457       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8458   }\r
8459 }\r
8460 \r
8461 \r
8462 VOID\r
8463 SetUserThinkingEnables()\r
8464 {\r
8465   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8466 }\r
8467 \r
8468 VOID\r
8469 SetMachineThinkingEnables()\r
8470 {\r
8471   HMENU hMenu = GetMenu(hwndMain);\r
8472   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8473 \r
8474   SetMenuEnables(hMenu, machineThinkingEnables);\r
8475 \r
8476   if (gameMode == MachinePlaysBlack) {\r
8477     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8478   } else if (gameMode == MachinePlaysWhite) {\r
8479     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8480   } else if (gameMode == TwoMachinesPlay) {\r
8481     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8482   }\r
8483 }\r
8484 \r
8485 \r
8486 VOID\r
8487 DisplayTitle(char *str)\r
8488 {\r
8489   char title[MSG_SIZ], *host;\r
8490   if (str[0] != NULLCHAR) {\r
8491     strcpy(title, str);\r
8492   } else if (appData.icsActive) {\r
8493     if (appData.icsCommPort[0] != NULLCHAR)\r
8494       host = "ICS";\r
8495     else \r
8496       host = appData.icsHost;\r
8497     sprintf(title, "%s: %s", szTitle, host);\r
8498   } else if (appData.noChessProgram) {\r
8499     strcpy(title, szTitle);\r
8500   } else {\r
8501     strcpy(title, szTitle);\r
8502     strcat(title, ": ");\r
8503     strcat(title, first.tidy);\r
8504   }\r
8505   SetWindowText(hwndMain, title);\r
8506 }\r
8507 \r
8508 \r
8509 VOID\r
8510 DisplayMessage(char *str1, char *str2)\r
8511 {\r
8512   HDC hdc;\r
8513   HFONT oldFont;\r
8514   int remain = MESSAGE_TEXT_MAX - 1;\r
8515   int len;\r
8516 \r
8517   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8518   messageText[0] = NULLCHAR;\r
8519   if (*str1) {\r
8520     len = strlen(str1);\r
8521     if (len > remain) len = remain;\r
8522     strncpy(messageText, str1, len);\r
8523     messageText[len] = NULLCHAR;\r
8524     remain -= len;\r
8525   }\r
8526   if (*str2 && remain >= 2) {\r
8527     if (*str1) {\r
8528       strcat(messageText, "  ");\r
8529       remain -= 2;\r
8530     }\r
8531     len = strlen(str2);\r
8532     if (len > remain) len = remain;\r
8533     strncat(messageText, str2, len);\r
8534   }\r
8535   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8536 \r
8537   if (IsIconic(hwndMain)) return;\r
8538   hdc = GetDC(hwndMain);\r
8539   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8540   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8541              &messageRect, messageText, strlen(messageText), NULL);\r
8542   (void) SelectObject(hdc, oldFont);\r
8543   (void) ReleaseDC(hwndMain, hdc);\r
8544 }\r
8545 \r
8546 VOID\r
8547 DisplayError(char *str, int error)\r
8548 {\r
8549   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8550   int len;\r
8551 \r
8552   if (error == 0) {\r
8553     strcpy(buf, str);\r
8554   } else {\r
8555     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8556                         NULL, error, LANG_NEUTRAL,\r
8557                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8558     if (len > 0) {\r
8559       sprintf(buf, "%s:\n%s", str, buf2);\r
8560     } else {\r
8561       ErrorMap *em = errmap;\r
8562       while (em->err != 0 && em->err != error) em++;\r
8563       if (em->err != 0) {\r
8564         sprintf(buf, "%s:\n%s", str, em->msg);\r
8565       } else {\r
8566         sprintf(buf, "%s:\nError code %d", str, error);\r
8567       }\r
8568     }\r
8569   }\r
8570   \r
8571   ErrorPopUp("Error", buf);\r
8572 }\r
8573 \r
8574 \r
8575 VOID\r
8576 DisplayMoveError(char *str)\r
8577 {\r
8578   fromX = fromY = -1;\r
8579   ClearHighlights();\r
8580   DrawPosition(FALSE, NULL);\r
8581   if (appData.popupMoveErrors) {\r
8582     ErrorPopUp("Error", str);\r
8583   } else {\r
8584     DisplayMessage(str, "");\r
8585     moveErrorMessageUp = TRUE;\r
8586   }\r
8587 }\r
8588 \r
8589 VOID\r
8590 DisplayFatalError(char *str, int error, int exitStatus)\r
8591 {\r
8592   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8593   int len;\r
8594   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8595 \r
8596   if (error != 0) {\r
8597     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8598                         NULL, error, LANG_NEUTRAL,\r
8599                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8600     if (len > 0) {\r
8601       sprintf(buf, "%s:\n%s", str, buf2);\r
8602     } else {\r
8603       ErrorMap *em = errmap;\r
8604       while (em->err != 0 && em->err != error) em++;\r
8605       if (em->err != 0) {\r
8606         sprintf(buf, "%s:\n%s", str, em->msg);\r
8607       } else {\r
8608         sprintf(buf, "%s:\nError code %d", str, error);\r
8609       }\r
8610     }\r
8611     str = buf;\r
8612   }\r
8613   if (appData.debugMode) {\r
8614     fprintf(debugFP, "%s: %s\n", label, str);\r
8615   }\r
8616   if (appData.popupExitMessage) {\r
8617     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8618                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8619   }\r
8620   ExitEvent(exitStatus);\r
8621 }\r
8622 \r
8623 \r
8624 VOID\r
8625 DisplayInformation(char *str)\r
8626 {\r
8627   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8628 }\r
8629 \r
8630 \r
8631 VOID\r
8632 DisplayNote(char *str)\r
8633 {\r
8634   ErrorPopUp("Note", str);\r
8635 }\r
8636 \r
8637 \r
8638 typedef struct {\r
8639   char *title, *question, *replyPrefix;\r
8640   ProcRef pr;\r
8641 } QuestionParams;\r
8642 \r
8643 LRESULT CALLBACK\r
8644 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8645 {\r
8646   static QuestionParams *qp;\r
8647   char reply[MSG_SIZ];\r
8648   int len, err;\r
8649 \r
8650   switch (message) {\r
8651   case WM_INITDIALOG:\r
8652     qp = (QuestionParams *) lParam;\r
8653     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8654     SetWindowText(hDlg, qp->title);\r
8655     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8656     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8657     return FALSE;\r
8658 \r
8659   case WM_COMMAND:\r
8660     switch (LOWORD(wParam)) {\r
8661     case IDOK:\r
8662       strcpy(reply, qp->replyPrefix);\r
8663       if (*reply) strcat(reply, " ");\r
8664       len = strlen(reply);\r
8665       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8666       strcat(reply, "\n");\r
8667       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8668       EndDialog(hDlg, TRUE);\r
8669       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8670       return TRUE;\r
8671     case IDCANCEL:\r
8672       EndDialog(hDlg, FALSE);\r
8673       return TRUE;\r
8674     default:\r
8675       break;\r
8676     }\r
8677     break;\r
8678   }\r
8679   return FALSE;\r
8680 }\r
8681 \r
8682 VOID\r
8683 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8684 {\r
8685     QuestionParams qp;\r
8686     FARPROC lpProc;\r
8687     \r
8688     qp.title = title;\r
8689     qp.question = question;\r
8690     qp.replyPrefix = replyPrefix;\r
8691     qp.pr = pr;\r
8692     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8693     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8694       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8695     FreeProcInstance(lpProc);\r
8696 }\r
8697 \r
8698 /* [AS] Pick FRC position */\r
8699 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8700 {\r
8701     static int * lpIndexFRC;\r
8702     BOOL index_is_ok;\r
8703     char buf[16];\r
8704 \r
8705     switch( message )\r
8706     {\r
8707     case WM_INITDIALOG:\r
8708         lpIndexFRC = (int *) lParam;\r
8709 \r
8710         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8711 \r
8712         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8713         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8714         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8715         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8716 \r
8717         break;\r
8718 \r
8719     case WM_COMMAND:\r
8720         switch( LOWORD(wParam) ) {\r
8721         case IDOK:\r
8722             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8723             EndDialog( hDlg, 0 );\r
8724             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8725             return TRUE;\r
8726         case IDCANCEL:\r
8727             EndDialog( hDlg, 1 );   \r
8728             return TRUE;\r
8729         case IDC_NFG_Edit:\r
8730             if( HIWORD(wParam) == EN_CHANGE ) {\r
8731                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8732 \r
8733                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8734             }\r
8735             return TRUE;\r
8736         case IDC_NFG_Random:\r
8737             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8738             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8739             return TRUE;\r
8740         }\r
8741 \r
8742         break;\r
8743     }\r
8744 \r
8745     return FALSE;\r
8746 }\r
8747 \r
8748 int NewGameFRC()\r
8749 {\r
8750     int result;\r
8751     int index = appData.defaultFrcPosition;\r
8752     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8753 \r
8754     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8755 \r
8756     if( result == 0 ) {\r
8757         appData.defaultFrcPosition = index;\r
8758     }\r
8759 \r
8760     return result;\r
8761 }\r
8762 \r
8763 /* [AS] Game list options */\r
8764 typedef struct {\r
8765     char id;\r
8766     char * name;\r
8767 } GLT_Item;\r
8768 \r
8769 static GLT_Item GLT_ItemInfo[] = {\r
8770     { GLT_EVENT,      "Event" },\r
8771     { GLT_SITE,       "Site" },\r
8772     { GLT_DATE,       "Date" },\r
8773     { GLT_ROUND,      "Round" },\r
8774     { GLT_PLAYERS,    "Players" },\r
8775     { GLT_RESULT,     "Result" },\r
8776     { GLT_WHITE_ELO,  "White Rating" },\r
8777     { GLT_BLACK_ELO,  "Black Rating" },\r
8778     { GLT_TIME_CONTROL,"Time Control" },\r
8779     { GLT_VARIANT,    "Variant" },\r
8780     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8781     { 0, 0 }\r
8782 };\r
8783 \r
8784 const char * GLT_FindItem( char id )\r
8785 {\r
8786     const char * result = 0;\r
8787 \r
8788     GLT_Item * list = GLT_ItemInfo;\r
8789 \r
8790     while( list->id != 0 ) {\r
8791         if( list->id == id ) {\r
8792             result = list->name;\r
8793             break;\r
8794         }\r
8795 \r
8796         list++;\r
8797     }\r
8798 \r
8799     return result;\r
8800 }\r
8801 \r
8802 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8803 {\r
8804     const char * name = GLT_FindItem( id );\r
8805 \r
8806     if( name != 0 ) {\r
8807         if( index >= 0 ) {\r
8808             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8809         }\r
8810         else {\r
8811             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8812         }\r
8813     }\r
8814 }\r
8815 \r
8816 void GLT_TagsToList( HWND hDlg, char * tags )\r
8817 {\r
8818     char * pc = tags;\r
8819 \r
8820     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8821 \r
8822     while( *pc ) {\r
8823         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8824         pc++;\r
8825     }\r
8826 \r
8827     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8828 \r
8829     pc = GLT_ALL_TAGS;\r
8830 \r
8831     while( *pc ) {\r
8832         if( strchr( tags, *pc ) == 0 ) {\r
8833             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8834         }\r
8835         pc++;\r
8836     }\r
8837 \r
8838     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8839 }\r
8840 \r
8841 char GLT_ListItemToTag( HWND hDlg, int index )\r
8842 {\r
8843     char result = '\0';\r
8844     char name[128];\r
8845 \r
8846     GLT_Item * list = GLT_ItemInfo;\r
8847 \r
8848     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8849         while( list->id != 0 ) {\r
8850             if( strcmp( list->name, name ) == 0 ) {\r
8851                 result = list->id;\r
8852                 break;\r
8853             }\r
8854 \r
8855             list++;\r
8856         }\r
8857     }\r
8858 \r
8859     return result;\r
8860 }\r
8861 \r
8862 void GLT_MoveSelection( HWND hDlg, int delta )\r
8863 {\r
8864     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8865     int idx2 = idx1 + delta;\r
8866     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8867 \r
8868     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8869         char buf[128];\r
8870 \r
8871         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8872         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8873         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8874         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8875     }\r
8876 }\r
8877 \r
8878 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8879 {\r
8880     static char glt[64];\r
8881     static char * lpUserGLT;\r
8882 \r
8883     switch( message )\r
8884     {\r
8885     case WM_INITDIALOG:\r
8886         lpUserGLT = (char *) lParam;\r
8887         \r
8888         strcpy( glt, lpUserGLT );\r
8889 \r
8890         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8891 \r
8892         /* Initialize list */\r
8893         GLT_TagsToList( hDlg, glt );\r
8894 \r
8895         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8896 \r
8897         break;\r
8898 \r
8899     case WM_COMMAND:\r
8900         switch( LOWORD(wParam) ) {\r
8901         case IDOK:\r
8902             {\r
8903                 char * pc = lpUserGLT;\r
8904                 int idx = 0;\r
8905                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8906                 char id;\r
8907 \r
8908                 do {\r
8909                     id = GLT_ListItemToTag( hDlg, idx );\r
8910 \r
8911                     *pc++ = id;\r
8912                     idx++;\r
8913                 } while( id != '\0' );\r
8914             }\r
8915             EndDialog( hDlg, 0 );\r
8916             return TRUE;\r
8917         case IDCANCEL:\r
8918             EndDialog( hDlg, 1 );\r
8919             return TRUE;\r
8920 \r
8921         case IDC_GLT_Default:\r
8922             strcpy( glt, GLT_DEFAULT_TAGS );\r
8923             GLT_TagsToList( hDlg, glt );\r
8924             return TRUE;\r
8925 \r
8926         case IDC_GLT_Restore:\r
8927             strcpy( glt, lpUserGLT );\r
8928             GLT_TagsToList( hDlg, glt );\r
8929             return TRUE;\r
8930 \r
8931         case IDC_GLT_Up:\r
8932             GLT_MoveSelection( hDlg, -1 );\r
8933             return TRUE;\r
8934 \r
8935         case IDC_GLT_Down:\r
8936             GLT_MoveSelection( hDlg, +1 );\r
8937             return TRUE;\r
8938         }\r
8939 \r
8940         break;\r
8941     }\r
8942 \r
8943     return FALSE;\r
8944 }\r
8945 \r
8946 int GameListOptions()\r
8947 {\r
8948     char glt[64];\r
8949     int result;\r
8950     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8951 \r
8952     strcpy( glt, appData.gameListTags );\r
8953 \r
8954     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8955 \r
8956     if( result == 0 ) {\r
8957         /* [AS] Memory leak here! */\r
8958         appData.gameListTags = strdup( glt ); \r
8959     }\r
8960 \r
8961     return result;\r
8962 }\r
8963 \r
8964 \r
8965 VOID\r
8966 DisplayIcsInteractionTitle(char *str)\r
8967 {\r
8968   char consoleTitle[MSG_SIZ];\r
8969 \r
8970   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8971   SetWindowText(hwndConsole, consoleTitle);\r
8972 }\r
8973 \r
8974 void\r
8975 DrawPosition(int fullRedraw, Board board)\r
8976 {\r
8977   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8978 }\r
8979 \r
8980 \r
8981 VOID\r
8982 ResetFrontEnd()\r
8983 {\r
8984   fromX = fromY = -1;\r
8985   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8986     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8987     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8988     dragInfo.lastpos = dragInfo.pos;\r
8989     dragInfo.start.x = dragInfo.start.y = -1;\r
8990     dragInfo.from = dragInfo.start;\r
8991     ReleaseCapture();\r
8992     DrawPosition(TRUE, NULL);\r
8993   }\r
8994 }\r
8995 \r
8996 \r
8997 VOID\r
8998 CommentPopUp(char *title, char *str)\r
8999 {\r
9000   HWND hwnd = GetActiveWindow();\r
9001   EitherCommentPopUp(0, title, str, FALSE);\r
9002   SetActiveWindow(hwnd);\r
9003 }\r
9004 \r
9005 VOID\r
9006 CommentPopDown(void)\r
9007 {\r
9008   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9009   if (commentDialog) {\r
9010     ShowWindow(commentDialog, SW_HIDE);\r
9011   }\r
9012   commentDialogUp = FALSE;\r
9013 }\r
9014 \r
9015 VOID\r
9016 EditCommentPopUp(int index, char *title, char *str)\r
9017 {\r
9018   EitherCommentPopUp(index, title, str, TRUE);\r
9019 }\r
9020 \r
9021 \r
9022 VOID\r
9023 RingBell()\r
9024 {\r
9025   MyPlaySound(&sounds[(int)SoundMove]);\r
9026 }\r
9027 \r
9028 VOID PlayIcsWinSound()\r
9029 {\r
9030   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9031 }\r
9032 \r
9033 VOID PlayIcsLossSound()\r
9034 {\r
9035   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9036 }\r
9037 \r
9038 VOID PlayIcsDrawSound()\r
9039 {\r
9040   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9041 }\r
9042 \r
9043 VOID PlayIcsUnfinishedSound()\r
9044 {\r
9045   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9046 }\r
9047 \r
9048 VOID\r
9049 PlayAlarmSound()\r
9050 {\r
9051   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9052 }\r
9053 \r
9054 \r
9055 VOID\r
9056 EchoOn()\r
9057 {\r
9058   HWND hInput;\r
9059   consoleEcho = TRUE;\r
9060   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9061   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9062   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9063 }\r
9064 \r
9065 \r
9066 VOID\r
9067 EchoOff()\r
9068 {\r
9069   CHARFORMAT cf;\r
9070   HWND hInput;\r
9071   consoleEcho = FALSE;\r
9072   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9073   /* This works OK: set text and background both to the same color */\r
9074   cf = consoleCF;\r
9075   cf.crTextColor = COLOR_ECHOOFF;\r
9076   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9077   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9078 }\r
9079 \r
9080 /* No Raw()...? */\r
9081 \r
9082 void Colorize(ColorClass cc, int continuation)\r
9083 {\r
9084   currentColorClass = cc;\r
9085   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9086   consoleCF.crTextColor = textAttribs[cc].color;\r
9087   consoleCF.dwEffects = textAttribs[cc].effects;\r
9088   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9089 }\r
9090 \r
9091 char *\r
9092 UserName()\r
9093 {\r
9094   static char buf[MSG_SIZ];\r
9095   DWORD bufsiz = MSG_SIZ;\r
9096 \r
9097   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9098         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9099   }\r
9100   if (!GetUserName(buf, &bufsiz)) {\r
9101     /*DisplayError("Error getting user name", GetLastError());*/\r
9102     strcpy(buf, "User");\r
9103   }\r
9104   return buf;\r
9105 }\r
9106 \r
9107 char *\r
9108 HostName()\r
9109 {\r
9110   static char buf[MSG_SIZ];\r
9111   DWORD bufsiz = MSG_SIZ;\r
9112 \r
9113   if (!GetComputerName(buf, &bufsiz)) {\r
9114     /*DisplayError("Error getting host name", GetLastError());*/\r
9115     strcpy(buf, "Unknown");\r
9116   }\r
9117   return buf;\r
9118 }\r
9119 \r
9120 \r
9121 int\r
9122 ClockTimerRunning()\r
9123 {\r
9124   return clockTimerEvent != 0;\r
9125 }\r
9126 \r
9127 int\r
9128 StopClockTimer()\r
9129 {\r
9130   if (clockTimerEvent == 0) return FALSE;\r
9131   KillTimer(hwndMain, clockTimerEvent);\r
9132   clockTimerEvent = 0;\r
9133   return TRUE;\r
9134 }\r
9135 \r
9136 void\r
9137 StartClockTimer(long millisec)\r
9138 {\r
9139   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9140                              (UINT) millisec, NULL);\r
9141 }\r
9142 \r
9143 void\r
9144 DisplayWhiteClock(long timeRemaining, int highlight)\r
9145 {\r
9146   HDC hdc;\r
9147   hdc = GetDC(hwndMain);\r
9148   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9149 \r
9150   if (!IsIconic(hwndMain)) {\r
9151     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
9152   }\r
9153   if (highlight && iconCurrent == iconBlack) {\r
9154     iconCurrent = iconWhite;\r
9155     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9156     if (IsIconic(hwndMain)) {\r
9157       DrawIcon(hdc, 2, 2, iconCurrent);\r
9158     }\r
9159   }\r
9160   (void) ReleaseDC(hwndMain, hdc);\r
9161   if (hwndConsole)\r
9162     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9163 }\r
9164 \r
9165 void\r
9166 DisplayBlackClock(long timeRemaining, int highlight)\r
9167 {\r
9168   HDC hdc;\r
9169   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9170 \r
9171   hdc = GetDC(hwndMain);\r
9172   if (!IsIconic(hwndMain)) {\r
9173     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9174   }\r
9175   if (highlight && iconCurrent == iconWhite) {\r
9176     iconCurrent = iconBlack;\r
9177     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9178     if (IsIconic(hwndMain)) {\r
9179       DrawIcon(hdc, 2, 2, iconCurrent);\r
9180     }\r
9181   }\r
9182   (void) ReleaseDC(hwndMain, hdc);\r
9183   if (hwndConsole)\r
9184     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9185 }\r
9186 \r
9187 \r
9188 int\r
9189 LoadGameTimerRunning()\r
9190 {\r
9191   return loadGameTimerEvent != 0;\r
9192 }\r
9193 \r
9194 int\r
9195 StopLoadGameTimer()\r
9196 {\r
9197   if (loadGameTimerEvent == 0) return FALSE;\r
9198   KillTimer(hwndMain, loadGameTimerEvent);\r
9199   loadGameTimerEvent = 0;\r
9200   return TRUE;\r
9201 }\r
9202 \r
9203 void\r
9204 StartLoadGameTimer(long millisec)\r
9205 {\r
9206   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9207                                 (UINT) millisec, NULL);\r
9208 }\r
9209 \r
9210 void\r
9211 AutoSaveGame()\r
9212 {\r
9213   char *defName;\r
9214   FILE *f;\r
9215   char fileTitle[MSG_SIZ];\r
9216 \r
9217   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9218   f = OpenFileDialog(hwndMain, "a", defName,\r
9219                      appData.oldSaveStyle ? "gam" : "pgn",\r
9220                      GAME_FILT, \r
9221                      "Save Game to File", NULL, fileTitle, NULL);\r
9222   if (f != NULL) {\r
9223     SaveGame(f, 0, "");\r
9224     fclose(f);\r
9225   }\r
9226 }\r
9227 \r
9228 \r
9229 void\r
9230 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9231 {\r
9232   if (delayedTimerEvent != 0) {\r
9233     if (appData.debugMode) {\r
9234       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9235     }\r
9236     KillTimer(hwndMain, delayedTimerEvent);\r
9237     delayedTimerEvent = 0;\r
9238     delayedTimerCallback();\r
9239   }\r
9240   delayedTimerCallback = cb;\r
9241   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9242                                 (UINT) millisec, NULL);\r
9243 }\r
9244 \r
9245 DelayedEventCallback\r
9246 GetDelayedEvent()\r
9247 {\r
9248   if (delayedTimerEvent) {\r
9249     return delayedTimerCallback;\r
9250   } else {\r
9251     return NULL;\r
9252   }\r
9253 }\r
9254 \r
9255 void\r
9256 CancelDelayedEvent()\r
9257 {\r
9258   if (delayedTimerEvent) {\r
9259     KillTimer(hwndMain, delayedTimerEvent);\r
9260     delayedTimerEvent = 0;\r
9261   }\r
9262 }\r
9263 \r
9264 /* Start a child process running the given program.\r
9265    The process's standard output can be read from "from", and its\r
9266    standard input can be written to "to".\r
9267    Exit with fatal error if anything goes wrong.\r
9268    Returns an opaque pointer that can be used to destroy the process\r
9269    later.\r
9270 */\r
9271 int\r
9272 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9273 {\r
9274 #define BUFSIZE 4096\r
9275 \r
9276   HANDLE hChildStdinRd, hChildStdinWr,\r
9277     hChildStdoutRd, hChildStdoutWr;\r
9278   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9279   SECURITY_ATTRIBUTES saAttr;\r
9280   BOOL fSuccess;\r
9281   PROCESS_INFORMATION piProcInfo;\r
9282   STARTUPINFO siStartInfo;\r
9283   ChildProc *cp;\r
9284   char buf[MSG_SIZ];\r
9285   DWORD err;\r
9286 \r
9287   if (appData.debugMode) {\r
9288     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9289   }\r
9290 \r
9291   *pr = NoProc;\r
9292 \r
9293   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9294   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9295   saAttr.bInheritHandle = TRUE;\r
9296   saAttr.lpSecurityDescriptor = NULL;\r
9297 \r
9298   /*\r
9299    * The steps for redirecting child's STDOUT:\r
9300    *     1. Create anonymous pipe to be STDOUT for child.\r
9301    *     2. Create a noninheritable duplicate of read handle,\r
9302    *         and close the inheritable read handle.\r
9303    */\r
9304 \r
9305   /* Create a pipe for the child's STDOUT. */\r
9306   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9307     return GetLastError();\r
9308   }\r
9309 \r
9310   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9311   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9312                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9313                              FALSE,     /* not inherited */\r
9314                              DUPLICATE_SAME_ACCESS);\r
9315   if (! fSuccess) {\r
9316     return GetLastError();\r
9317   }\r
9318   CloseHandle(hChildStdoutRd);\r
9319 \r
9320   /*\r
9321    * The steps for redirecting child's STDIN:\r
9322    *     1. Create anonymous pipe to be STDIN for child.\r
9323    *     2. Create a noninheritable duplicate of write handle,\r
9324    *         and close the inheritable write handle.\r
9325    */\r
9326 \r
9327   /* Create a pipe for the child's STDIN. */\r
9328   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9329     return GetLastError();\r
9330   }\r
9331 \r
9332   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9333   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9334                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9335                              FALSE,     /* not inherited */\r
9336                              DUPLICATE_SAME_ACCESS);\r
9337   if (! fSuccess) {\r
9338     return GetLastError();\r
9339   }\r
9340   CloseHandle(hChildStdinWr);\r
9341 \r
9342   /* Arrange to (1) look in dir for the child .exe file, and\r
9343    * (2) have dir be the child's working directory.  Interpret\r
9344    * dir relative to the directory WinBoard loaded from. */\r
9345   GetCurrentDirectory(MSG_SIZ, buf);\r
9346   SetCurrentDirectory(installDir);\r
9347   SetCurrentDirectory(dir);\r
9348 \r
9349   /* Now create the child process. */\r
9350 \r
9351   siStartInfo.cb = sizeof(STARTUPINFO);\r
9352   siStartInfo.lpReserved = NULL;\r
9353   siStartInfo.lpDesktop = NULL;\r
9354   siStartInfo.lpTitle = NULL;\r
9355   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9356   siStartInfo.cbReserved2 = 0;\r
9357   siStartInfo.lpReserved2 = NULL;\r
9358   siStartInfo.hStdInput = hChildStdinRd;\r
9359   siStartInfo.hStdOutput = hChildStdoutWr;\r
9360   siStartInfo.hStdError = hChildStdoutWr;\r
9361 \r
9362   fSuccess = CreateProcess(NULL,\r
9363                            cmdLine,        /* command line */\r
9364                            NULL,           /* process security attributes */\r
9365                            NULL,           /* primary thread security attrs */\r
9366                            TRUE,           /* handles are inherited */\r
9367                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9368                            NULL,           /* use parent's environment */\r
9369                            NULL,\r
9370                            &siStartInfo, /* STARTUPINFO pointer */\r
9371                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9372 \r
9373   err = GetLastError();\r
9374   SetCurrentDirectory(buf); /* return to prev directory */\r
9375   if (! fSuccess) {\r
9376     return err;\r
9377   }\r
9378 \r
9379   /* Close the handles we don't need in the parent */\r
9380   CloseHandle(piProcInfo.hThread);\r
9381   CloseHandle(hChildStdinRd);\r
9382   CloseHandle(hChildStdoutWr);\r
9383 \r
9384   /* Prepare return value */\r
9385   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9386   cp->kind = CPReal;\r
9387   cp->hProcess = piProcInfo.hProcess;\r
9388   cp->pid = piProcInfo.dwProcessId;\r
9389   cp->hFrom = hChildStdoutRdDup;\r
9390   cp->hTo = hChildStdinWrDup;\r
9391 \r
9392   *pr = (void *) cp;\r
9393 \r
9394   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9395      2000 where engines sometimes don't see the initial command(s)\r
9396      from WinBoard and hang.  I don't understand how that can happen,\r
9397      but the Sleep is harmless, so I've put it in.  Others have also\r
9398      reported what may be the same problem, so hopefully this will fix\r
9399      it for them too.  */\r
9400   Sleep(500);\r
9401 \r
9402   return NO_ERROR;\r
9403 }\r
9404 \r
9405 \r
9406 void\r
9407 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9408 {\r
9409   ChildProc *cp; int result;\r
9410 \r
9411   cp = (ChildProc *) pr;\r
9412   if (cp == NULL) return;\r
9413 \r
9414   switch (cp->kind) {\r
9415   case CPReal:\r
9416     /* TerminateProcess is considered harmful, so... */\r
9417     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9418     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9419     /* The following doesn't work because the chess program\r
9420        doesn't "have the same console" as WinBoard.  Maybe\r
9421        we could arrange for this even though neither WinBoard\r
9422        nor the chess program uses a console for stdio? */\r
9423     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9424 \r
9425     /* [AS] Special termination modes for misbehaving programs... */\r
9426     if( signal == 9 ) { \r
9427         result = TerminateProcess( cp->hProcess, 0 );\r
9428 \r
9429         if ( appData.debugMode) {\r
9430             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9431         }\r
9432     }\r
9433     else if( signal == 10 ) {\r
9434         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9435 \r
9436         if( dw != WAIT_OBJECT_0 ) {\r
9437             result = TerminateProcess( cp->hProcess, 0 );\r
9438 \r
9439             if ( appData.debugMode) {\r
9440                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9441             }\r
9442 \r
9443         }\r
9444     }\r
9445 \r
9446     CloseHandle(cp->hProcess);\r
9447     break;\r
9448 \r
9449   case CPComm:\r
9450     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9451     break;\r
9452 \r
9453   case CPSock:\r
9454     closesocket(cp->sock);\r
9455     WSACleanup();\r
9456     break;\r
9457 \r
9458   case CPRcmd:\r
9459     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9460     closesocket(cp->sock);\r
9461     closesocket(cp->sock2);\r
9462     WSACleanup();\r
9463     break;\r
9464   }\r
9465   free(cp);\r
9466 }\r
9467 \r
9468 void\r
9469 InterruptChildProcess(ProcRef pr)\r
9470 {\r
9471   ChildProc *cp;\r
9472 \r
9473   cp = (ChildProc *) pr;\r
9474   if (cp == NULL) return;\r
9475   switch (cp->kind) {\r
9476   case CPReal:\r
9477     /* The following doesn't work because the chess program\r
9478        doesn't "have the same console" as WinBoard.  Maybe\r
9479        we could arrange for this even though neither WinBoard\r
9480        nor the chess program uses a console for stdio */\r
9481     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9482     break;\r
9483 \r
9484   case CPComm:\r
9485   case CPSock:\r
9486     /* Can't interrupt */\r
9487     break;\r
9488 \r
9489   case CPRcmd:\r
9490     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9491     break;\r
9492   }\r
9493 }\r
9494 \r
9495 \r
9496 int\r
9497 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9498 {\r
9499   char cmdLine[MSG_SIZ];\r
9500 \r
9501   if (port[0] == NULLCHAR) {\r
9502     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9503   } else {\r
9504     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9505   }\r
9506   return StartChildProcess(cmdLine, "", pr);\r
9507 }\r
9508 \r
9509 \r
9510 /* Code to open TCP sockets */\r
9511 \r
9512 int\r
9513 OpenTCP(char *host, char *port, ProcRef *pr)\r
9514 {\r
9515   ChildProc *cp;\r
9516   int err;\r
9517   SOCKET s;\r
9518   struct sockaddr_in sa, mysa;\r
9519   struct hostent FAR *hp;\r
9520   unsigned short uport;\r
9521   WORD wVersionRequested;\r
9522   WSADATA wsaData;\r
9523 \r
9524   /* Initialize socket DLL */\r
9525   wVersionRequested = MAKEWORD(1, 1);\r
9526   err = WSAStartup(wVersionRequested, &wsaData);\r
9527   if (err != 0) return err;\r
9528 \r
9529   /* Make socket */\r
9530   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9531     err = WSAGetLastError();\r
9532     WSACleanup();\r
9533     return err;\r
9534   }\r
9535 \r
9536   /* Bind local address using (mostly) don't-care values.\r
9537    */\r
9538   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9539   mysa.sin_family = AF_INET;\r
9540   mysa.sin_addr.s_addr = INADDR_ANY;\r
9541   uport = (unsigned short) 0;\r
9542   mysa.sin_port = htons(uport);\r
9543   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9544       == SOCKET_ERROR) {\r
9545     err = WSAGetLastError();\r
9546     WSACleanup();\r
9547     return err;\r
9548   }\r
9549 \r
9550   /* Resolve remote host name */\r
9551   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9552   if (!(hp = gethostbyname(host))) {\r
9553     unsigned int b0, b1, b2, b3;\r
9554 \r
9555     err = WSAGetLastError();\r
9556 \r
9557     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9558       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9559       hp->h_addrtype = AF_INET;\r
9560       hp->h_length = 4;\r
9561       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9562       hp->h_addr_list[0] = (char *) malloc(4);\r
9563       hp->h_addr_list[0][0] = (char) b0;\r
9564       hp->h_addr_list[0][1] = (char) b1;\r
9565       hp->h_addr_list[0][2] = (char) b2;\r
9566       hp->h_addr_list[0][3] = (char) b3;\r
9567     } else {\r
9568       WSACleanup();\r
9569       return err;\r
9570     }\r
9571   }\r
9572   sa.sin_family = hp->h_addrtype;\r
9573   uport = (unsigned short) atoi(port);\r
9574   sa.sin_port = htons(uport);\r
9575   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9576 \r
9577   /* Make connection */\r
9578   if (connect(s, (struct sockaddr *) &sa,\r
9579               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9580     err = WSAGetLastError();\r
9581     WSACleanup();\r
9582     return err;\r
9583   }\r
9584 \r
9585   /* Prepare return value */\r
9586   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9587   cp->kind = CPSock;\r
9588   cp->sock = s;\r
9589   *pr = (ProcRef *) cp;\r
9590 \r
9591   return NO_ERROR;\r
9592 }\r
9593 \r
9594 int\r
9595 OpenCommPort(char *name, ProcRef *pr)\r
9596 {\r
9597   HANDLE h;\r
9598   COMMTIMEOUTS ct;\r
9599   ChildProc *cp;\r
9600   char fullname[MSG_SIZ];\r
9601 \r
9602   if (*name != '\\')\r
9603     sprintf(fullname, "\\\\.\\%s", name);\r
9604   else\r
9605     strcpy(fullname, name);\r
9606 \r
9607   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9608                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9609   if (h == (HANDLE) -1) {\r
9610     return GetLastError();\r
9611   }\r
9612   hCommPort = h;\r
9613 \r
9614   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9615 \r
9616   /* Accumulate characters until a 100ms pause, then parse */\r
9617   ct.ReadIntervalTimeout = 100;\r
9618   ct.ReadTotalTimeoutMultiplier = 0;\r
9619   ct.ReadTotalTimeoutConstant = 0;\r
9620   ct.WriteTotalTimeoutMultiplier = 0;\r
9621   ct.WriteTotalTimeoutConstant = 0;\r
9622   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9623 \r
9624   /* Prepare return value */\r
9625   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9626   cp->kind = CPComm;\r
9627   cp->hFrom = h;\r
9628   cp->hTo = h;\r
9629   *pr = (ProcRef *) cp;\r
9630 \r
9631   return NO_ERROR;\r
9632 }\r
9633 \r
9634 int\r
9635 OpenLoopback(ProcRef *pr)\r
9636 {\r
9637   DisplayFatalError("Not implemented", 0, 1);\r
9638   return NO_ERROR;\r
9639 }\r
9640 \r
9641 \r
9642 int\r
9643 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9644 {\r
9645   ChildProc *cp;\r
9646   int err;\r
9647   SOCKET s, s2, s3;\r
9648   struct sockaddr_in sa, mysa;\r
9649   struct hostent FAR *hp;\r
9650   unsigned short uport;\r
9651   WORD wVersionRequested;\r
9652   WSADATA wsaData;\r
9653   int fromPort;\r
9654   char stderrPortStr[MSG_SIZ];\r
9655 \r
9656   /* Initialize socket DLL */\r
9657   wVersionRequested = MAKEWORD(1, 1);\r
9658   err = WSAStartup(wVersionRequested, &wsaData);\r
9659   if (err != 0) return err;\r
9660 \r
9661   /* Resolve remote host name */\r
9662   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9663   if (!(hp = gethostbyname(host))) {\r
9664     unsigned int b0, b1, b2, b3;\r
9665 \r
9666     err = WSAGetLastError();\r
9667 \r
9668     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9669       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9670       hp->h_addrtype = AF_INET;\r
9671       hp->h_length = 4;\r
9672       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9673       hp->h_addr_list[0] = (char *) malloc(4);\r
9674       hp->h_addr_list[0][0] = (char) b0;\r
9675       hp->h_addr_list[0][1] = (char) b1;\r
9676       hp->h_addr_list[0][2] = (char) b2;\r
9677       hp->h_addr_list[0][3] = (char) b3;\r
9678     } else {\r
9679       WSACleanup();\r
9680       return err;\r
9681     }\r
9682   }\r
9683   sa.sin_family = hp->h_addrtype;\r
9684   uport = (unsigned short) 514;\r
9685   sa.sin_port = htons(uport);\r
9686   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9687 \r
9688   /* Bind local socket to unused "privileged" port address\r
9689    */\r
9690   s = INVALID_SOCKET;\r
9691   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9692   mysa.sin_family = AF_INET;\r
9693   mysa.sin_addr.s_addr = INADDR_ANY;\r
9694   for (fromPort = 1023;; fromPort--) {\r
9695     if (fromPort < 0) {\r
9696       WSACleanup();\r
9697       return WSAEADDRINUSE;\r
9698     }\r
9699     if (s == INVALID_SOCKET) {\r
9700       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9701         err = WSAGetLastError();\r
9702         WSACleanup();\r
9703         return err;\r
9704       }\r
9705     }\r
9706     uport = (unsigned short) fromPort;\r
9707     mysa.sin_port = htons(uport);\r
9708     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9709         == SOCKET_ERROR) {\r
9710       err = WSAGetLastError();\r
9711       if (err == WSAEADDRINUSE) continue;\r
9712       WSACleanup();\r
9713       return err;\r
9714     }\r
9715     if (connect(s, (struct sockaddr *) &sa,\r
9716       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9717       err = WSAGetLastError();\r
9718       if (err == WSAEADDRINUSE) {\r
9719         closesocket(s);\r
9720         s = -1;\r
9721         continue;\r
9722       }\r
9723       WSACleanup();\r
9724       return err;\r
9725     }\r
9726     break;\r
9727   }\r
9728 \r
9729   /* Bind stderr local socket to unused "privileged" port address\r
9730    */\r
9731   s2 = INVALID_SOCKET;\r
9732   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9733   mysa.sin_family = AF_INET;\r
9734   mysa.sin_addr.s_addr = INADDR_ANY;\r
9735   for (fromPort = 1023;; fromPort--) {\r
9736     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9737     if (fromPort < 0) {\r
9738       (void) closesocket(s);\r
9739       WSACleanup();\r
9740       return WSAEADDRINUSE;\r
9741     }\r
9742     if (s2 == INVALID_SOCKET) {\r
9743       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9744         err = WSAGetLastError();\r
9745         closesocket(s);\r
9746         WSACleanup();\r
9747         return err;\r
9748       }\r
9749     }\r
9750     uport = (unsigned short) fromPort;\r
9751     mysa.sin_port = htons(uport);\r
9752     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9753         == SOCKET_ERROR) {\r
9754       err = WSAGetLastError();\r
9755       if (err == WSAEADDRINUSE) continue;\r
9756       (void) closesocket(s);\r
9757       WSACleanup();\r
9758       return err;\r
9759     }\r
9760     if (listen(s2, 1) == SOCKET_ERROR) {\r
9761       err = WSAGetLastError();\r
9762       if (err == WSAEADDRINUSE) {\r
9763         closesocket(s2);\r
9764         s2 = INVALID_SOCKET;\r
9765         continue;\r
9766       }\r
9767       (void) closesocket(s);\r
9768       (void) closesocket(s2);\r
9769       WSACleanup();\r
9770       return err;\r
9771     }\r
9772     break;\r
9773   }\r
9774   prevStderrPort = fromPort; // remember port used\r
9775   sprintf(stderrPortStr, "%d", fromPort);\r
9776 \r
9777   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9778     err = WSAGetLastError();\r
9779     (void) closesocket(s);\r
9780     (void) closesocket(s2);\r
9781     WSACleanup();\r
9782     return err;\r
9783   }\r
9784 \r
9785   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9786     err = WSAGetLastError();\r
9787     (void) closesocket(s);\r
9788     (void) closesocket(s2);\r
9789     WSACleanup();\r
9790     return err;\r
9791   }\r
9792   if (*user == NULLCHAR) user = UserName();\r
9793   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9794     err = WSAGetLastError();\r
9795     (void) closesocket(s);\r
9796     (void) closesocket(s2);\r
9797     WSACleanup();\r
9798     return err;\r
9799   }\r
9800   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9801     err = WSAGetLastError();\r
9802     (void) closesocket(s);\r
9803     (void) closesocket(s2);\r
9804     WSACleanup();\r
9805     return err;\r
9806   }\r
9807 \r
9808   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9809     err = WSAGetLastError();\r
9810     (void) closesocket(s);\r
9811     (void) closesocket(s2);\r
9812     WSACleanup();\r
9813     return err;\r
9814   }\r
9815   (void) closesocket(s2);  /* Stop listening */\r
9816 \r
9817   /* Prepare return value */\r
9818   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9819   cp->kind = CPRcmd;\r
9820   cp->sock = s;\r
9821   cp->sock2 = s3;\r
9822   *pr = (ProcRef *) cp;\r
9823 \r
9824   return NO_ERROR;\r
9825 }\r
9826 \r
9827 \r
9828 InputSourceRef\r
9829 AddInputSource(ProcRef pr, int lineByLine,\r
9830                InputCallback func, VOIDSTAR closure)\r
9831 {\r
9832   InputSource *is, *is2 = NULL;\r
9833   ChildProc *cp = (ChildProc *) pr;\r
9834 \r
9835   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9836   is->lineByLine = lineByLine;\r
9837   is->func = func;\r
9838   is->closure = closure;\r
9839   is->second = NULL;\r
9840   is->next = is->buf;\r
9841   if (pr == NoProc) {\r
9842     is->kind = CPReal;\r
9843     consoleInputSource = is;\r
9844   } else {\r
9845     is->kind = cp->kind;\r
9846     /* \r
9847         [AS] Try to avoid a race condition if the thread is given control too early:\r
9848         we create all threads suspended so that the is->hThread variable can be\r
9849         safely assigned, then let the threads start with ResumeThread.\r
9850     */\r
9851     switch (cp->kind) {\r
9852     case CPReal:\r
9853       is->hFile = cp->hFrom;\r
9854       cp->hFrom = NULL; /* now owned by InputThread */\r
9855       is->hThread =\r
9856         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9857                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9858       break;\r
9859 \r
9860     case CPComm:\r
9861       is->hFile = cp->hFrom;\r
9862       cp->hFrom = NULL; /* now owned by InputThread */\r
9863       is->hThread =\r
9864         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9865                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9866       break;\r
9867 \r
9868     case CPSock:\r
9869       is->sock = cp->sock;\r
9870       is->hThread =\r
9871         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9872                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9873       break;\r
9874 \r
9875     case CPRcmd:\r
9876       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9877       *is2 = *is;\r
9878       is->sock = cp->sock;\r
9879       is->second = is2;\r
9880       is2->sock = cp->sock2;\r
9881       is2->second = is2;\r
9882       is->hThread =\r
9883         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9884                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9885       is2->hThread =\r
9886         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9887                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9888       break;\r
9889     }\r
9890 \r
9891     if( is->hThread != NULL ) {\r
9892         ResumeThread( is->hThread );\r
9893     }\r
9894 \r
9895     if( is2 != NULL && is2->hThread != NULL ) {\r
9896         ResumeThread( is2->hThread );\r
9897     }\r
9898   }\r
9899 \r
9900   return (InputSourceRef) is;\r
9901 }\r
9902 \r
9903 void\r
9904 RemoveInputSource(InputSourceRef isr)\r
9905 {\r
9906   InputSource *is;\r
9907 \r
9908   is = (InputSource *) isr;\r
9909   is->hThread = NULL;  /* tell thread to stop */\r
9910   CloseHandle(is->hThread);\r
9911   if (is->second != NULL) {\r
9912     is->second->hThread = NULL;\r
9913     CloseHandle(is->second->hThread);\r
9914   }\r
9915 }\r
9916 \r
9917 \r
9918 int\r
9919 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9920 {\r
9921   DWORD dOutCount;\r
9922   int outCount = SOCKET_ERROR;\r
9923   ChildProc *cp = (ChildProc *) pr;\r
9924   static OVERLAPPED ovl;\r
9925 \r
9926   if (pr == NoProc) {\r
9927     ConsoleOutput(message, count, FALSE);\r
9928     return count;\r
9929   } \r
9930 \r
9931   if (ovl.hEvent == NULL) {\r
9932     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9933   }\r
9934   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9935 \r
9936   switch (cp->kind) {\r
9937   case CPSock:\r
9938   case CPRcmd:\r
9939     outCount = send(cp->sock, message, count, 0);\r
9940     if (outCount == SOCKET_ERROR) {\r
9941       *outError = WSAGetLastError();\r
9942     } else {\r
9943       *outError = NO_ERROR;\r
9944     }\r
9945     break;\r
9946 \r
9947   case CPReal:\r
9948     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9949                   &dOutCount, NULL)) {\r
9950       *outError = NO_ERROR;\r
9951       outCount = (int) dOutCount;\r
9952     } else {\r
9953       *outError = GetLastError();\r
9954     }\r
9955     break;\r
9956 \r
9957   case CPComm:\r
9958     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9959                             &dOutCount, &ovl);\r
9960     if (*outError == NO_ERROR) {\r
9961       outCount = (int) dOutCount;\r
9962     }\r
9963     break;\r
9964   }\r
9965   return outCount;\r
9966 }\r
9967 \r
9968 int\r
9969 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9970                        long msdelay)\r
9971 {\r
9972   /* Ignore delay, not implemented for WinBoard */\r
9973   return OutputToProcess(pr, message, count, outError);\r
9974 }\r
9975 \r
9976 \r
9977 void\r
9978 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9979                         char *buf, int count, int error)\r
9980 {\r
9981   DisplayFatalError("Not implemented", 0, 1);\r
9982 }\r
9983 \r
9984 /* see wgamelist.c for Game List functions */\r
9985 /* see wedittags.c for Edit Tags functions */\r
9986 \r
9987 \r
9988 VOID\r
9989 ICSInitScript()\r
9990 {\r
9991   FILE *f;\r
9992   char buf[MSG_SIZ];\r
9993   char *dummy;\r
9994 \r
9995   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9996     f = fopen(buf, "r");\r
9997     if (f != NULL) {\r
9998       ProcessICSInitScript(f);\r
9999       fclose(f);\r
10000     }\r
10001   }\r
10002 }\r
10003 \r
10004 \r
10005 VOID\r
10006 StartAnalysisClock()\r
10007 {\r
10008   if (analysisTimerEvent) return;\r
10009   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10010                                         (UINT) 2000, NULL);\r
10011 }\r
10012 \r
10013 LRESULT CALLBACK\r
10014 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10015 {\r
10016   static HANDLE hwndText;\r
10017   RECT rect;\r
10018   static int sizeX, sizeY;\r
10019   int newSizeX, newSizeY, flags;\r
10020   MINMAXINFO *mmi;\r
10021 \r
10022   switch (message) {\r
10023   case WM_INITDIALOG: /* message: initialize dialog box */\r
10024     /* Initialize the dialog items */\r
10025     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10026     SetWindowText(hDlg, analysisTitle);\r
10027     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10028     /* Size and position the dialog */\r
10029     if (!analysisDialog) {\r
10030       analysisDialog = hDlg;\r
10031       flags = SWP_NOZORDER;\r
10032       GetClientRect(hDlg, &rect);\r
10033       sizeX = rect.right;\r
10034       sizeY = rect.bottom;\r
10035       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10036           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10037         WINDOWPLACEMENT wp;\r
10038         EnsureOnScreen(&analysisX, &analysisY);\r
10039         wp.length = sizeof(WINDOWPLACEMENT);\r
10040         wp.flags = 0;\r
10041         wp.showCmd = SW_SHOW;\r
10042         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10043         wp.rcNormalPosition.left = analysisX;\r
10044         wp.rcNormalPosition.right = analysisX + analysisW;\r
10045         wp.rcNormalPosition.top = analysisY;\r
10046         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10047         SetWindowPlacement(hDlg, &wp);\r
10048 \r
10049         GetClientRect(hDlg, &rect);\r
10050         newSizeX = rect.right;\r
10051         newSizeY = rect.bottom;\r
10052         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10053                               newSizeX, newSizeY);\r
10054         sizeX = newSizeX;\r
10055         sizeY = newSizeY;\r
10056       }\r
10057     }\r
10058     return FALSE;\r
10059 \r
10060   case WM_COMMAND: /* message: received a command */\r
10061     switch (LOWORD(wParam)) {\r
10062     case IDCANCEL:\r
10063       EditGameEvent();\r
10064       return TRUE;\r
10065     default:\r
10066       break;\r
10067     }\r
10068     break;\r
10069 \r
10070   case WM_SIZE:\r
10071     newSizeX = LOWORD(lParam);\r
10072     newSizeY = HIWORD(lParam);\r
10073     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10074     sizeX = newSizeX;\r
10075     sizeY = newSizeY;\r
10076     break;\r
10077 \r
10078   case WM_GETMINMAXINFO:\r
10079     /* Prevent resizing window too small */\r
10080     mmi = (MINMAXINFO *) lParam;\r
10081     mmi->ptMinTrackSize.x = 100;\r
10082     mmi->ptMinTrackSize.y = 100;\r
10083     break;\r
10084   }\r
10085   return FALSE;\r
10086 }\r
10087 \r
10088 VOID\r
10089 AnalysisPopUp(char* title, char* str)\r
10090 {\r
10091   FARPROC lpProc;\r
10092   char *p, *q;\r
10093 \r
10094   /* [AS] */\r
10095   EngineOutputPopUp();\r
10096   return;\r
10097 \r
10098   if (str == NULL) str = "";\r
10099   p = (char *) malloc(2 * strlen(str) + 2);\r
10100   q = p;\r
10101   while (*str) {\r
10102     if (*str == '\n') *q++ = '\r';\r
10103     *q++ = *str++;\r
10104   }\r
10105   *q = NULLCHAR;\r
10106   if (analysisText != NULL) free(analysisText);\r
10107   analysisText = p;\r
10108 \r
10109   if (analysisDialog) {\r
10110     SetWindowText(analysisDialog, title);\r
10111     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10112     ShowWindow(analysisDialog, SW_SHOW);\r
10113   } else {\r
10114     analysisTitle = title;\r
10115     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10116     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10117                  hwndMain, (DLGPROC)lpProc);\r
10118     FreeProcInstance(lpProc);\r
10119   }\r
10120   analysisDialogUp = TRUE;  \r
10121 }\r
10122 \r
10123 VOID\r
10124 AnalysisPopDown()\r
10125 {\r
10126   if (analysisDialog) {\r
10127     ShowWindow(analysisDialog, SW_HIDE);\r
10128   }\r
10129   analysisDialogUp = FALSE;  \r
10130 }\r
10131 \r
10132 \r
10133 VOID\r
10134 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10135 {\r
10136   highlightInfo.sq[0].x = fromX;\r
10137   highlightInfo.sq[0].y = fromY;\r
10138   highlightInfo.sq[1].x = toX;\r
10139   highlightInfo.sq[1].y = toY;\r
10140 }\r
10141 \r
10142 VOID\r
10143 ClearHighlights()\r
10144 {\r
10145   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10146     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10147 }\r
10148 \r
10149 VOID\r
10150 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10151 {\r
10152   premoveHighlightInfo.sq[0].x = fromX;\r
10153   premoveHighlightInfo.sq[0].y = fromY;\r
10154   premoveHighlightInfo.sq[1].x = toX;\r
10155   premoveHighlightInfo.sq[1].y = toY;\r
10156 }\r
10157 \r
10158 VOID\r
10159 ClearPremoveHighlights()\r
10160 {\r
10161   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10162     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10163 }\r
10164 \r
10165 VOID\r
10166 ShutDownFrontEnd()\r
10167 {\r
10168   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10169   DeleteClipboardTempFiles();\r
10170 }\r
10171 \r
10172 void\r
10173 BoardToTop()\r
10174 {\r
10175     if (IsIconic(hwndMain))\r
10176       ShowWindow(hwndMain, SW_RESTORE);\r
10177 \r
10178     SetActiveWindow(hwndMain);\r
10179 }\r
10180 \r
10181 /*\r
10182  * Prototypes for animation support routines\r
10183  */\r
10184 static void ScreenSquare(int column, int row, POINT * pt);\r
10185 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10186      POINT frames[], int * nFrames);\r
10187 \r
10188 \r
10189 #define kFactor 4\r
10190 \r
10191 void\r
10192 AnimateMove(board, fromX, fromY, toX, toY)\r
10193      Board board;\r
10194      int fromX;\r
10195      int fromY;\r
10196      int toX;\r
10197      int toY;\r
10198 {\r
10199   ChessSquare piece;\r
10200   POINT start, finish, mid;\r
10201   POINT frames[kFactor * 2 + 1];\r
10202   int nFrames, n;\r
10203 \r
10204   if (!appData.animate) return;\r
10205   if (doingSizing) return;\r
10206   if (fromY < 0 || fromX < 0) return;\r
10207   piece = board[fromY][fromX];\r
10208   if (piece >= EmptySquare) return;\r
10209 \r
10210   ScreenSquare(fromX, fromY, &start);\r
10211   ScreenSquare(toX, toY, &finish);\r
10212 \r
10213   /* All pieces except knights move in straight line */\r
10214   if (piece != WhiteKnight && piece != BlackKnight) {\r
10215     mid.x = start.x + (finish.x - start.x) / 2;\r
10216     mid.y = start.y + (finish.y - start.y) / 2;\r
10217   } else {\r
10218     /* Knight: make diagonal movement then straight */\r
10219     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10220        mid.x = start.x + (finish.x - start.x) / 2;\r
10221        mid.y = finish.y;\r
10222      } else {\r
10223        mid.x = finish.x;\r
10224        mid.y = start.y + (finish.y - start.y) / 2;\r
10225      }\r
10226   }\r
10227   \r
10228   /* Don't use as many frames for very short moves */\r
10229   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10230     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10231   else\r
10232     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10233 \r
10234   animInfo.from.x = fromX;\r
10235   animInfo.from.y = fromY;\r
10236   animInfo.to.x = toX;\r
10237   animInfo.to.y = toY;\r
10238   animInfo.lastpos = start;\r
10239   animInfo.piece = piece;\r
10240   for (n = 0; n < nFrames; n++) {\r
10241     animInfo.pos = frames[n];\r
10242     DrawPosition(FALSE, NULL);\r
10243     animInfo.lastpos = animInfo.pos;\r
10244     Sleep(appData.animSpeed);\r
10245   }\r
10246   animInfo.pos = finish;\r
10247   DrawPosition(FALSE, NULL);\r
10248   animInfo.piece = EmptySquare;\r
10249 }\r
10250 \r
10251 /*      Convert board position to corner of screen rect and color       */\r
10252 \r
10253 static void\r
10254 ScreenSquare(column, row, pt)\r
10255      int column; int row; POINT * pt;\r
10256 {\r
10257   if (flipView) {\r
10258     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10259     pt->y = lineGap + row * (squareSize + lineGap);\r
10260   } else {\r
10261     pt->x = lineGap + column * (squareSize + lineGap);\r
10262     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10263   }\r
10264 }\r
10265 \r
10266 /*      Generate a series of frame coords from start->mid->finish.\r
10267         The movement rate doubles until the half way point is\r
10268         reached, then halves back down to the final destination,\r
10269         which gives a nice slow in/out effect. The algorithmn\r
10270         may seem to generate too many intermediates for short\r
10271         moves, but remember that the purpose is to attract the\r
10272         viewers attention to the piece about to be moved and\r
10273         then to where it ends up. Too few frames would be less\r
10274         noticeable.                                             */\r
10275 \r
10276 static void\r
10277 Tween(start, mid, finish, factor, frames, nFrames)\r
10278      POINT * start; POINT * mid;\r
10279      POINT * finish; int factor;\r
10280      POINT frames[]; int * nFrames;\r
10281 {\r
10282   int n, fraction = 1, count = 0;\r
10283 \r
10284   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10285   for (n = 0; n < factor; n++)\r
10286     fraction *= 2;\r
10287   for (n = 0; n < factor; n++) {\r
10288     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10289     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10290     count ++;\r
10291     fraction = fraction / 2;\r
10292   }\r
10293   \r
10294   /* Midpoint */\r
10295   frames[count] = *mid;\r
10296   count ++;\r
10297   \r
10298   /* Slow out, stepping 1/2, then 1/4, ... */\r
10299   fraction = 2;\r
10300   for (n = 0; n < factor; n++) {\r
10301     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10302     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10303     count ++;\r
10304     fraction = fraction * 2;\r
10305   }\r
10306   *nFrames = count;\r
10307 }\r
10308 \r
10309 void\r
10310 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10311 {\r
10312 #if 0\r
10313     char buf[256];\r
10314 \r
10315     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10316         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10317 \r
10318     OutputDebugString( buf );\r
10319 #endif\r
10320 \r
10321     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10322 \r
10323     EvalGraphSet( first, last, current, pvInfoList );\r
10324 }\r
10325 \r
10326 void SetProgramStats( FrontEndProgramStats * stats )\r
10327 {\r
10328 #if 0\r
10329     char buf[1024];\r
10330 \r
10331     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10332         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10333 \r
10334     OutputDebugString( buf );\r
10335 #endif\r
10336 \r
10337     EngineOutputUpdate( stats );\r
10338 }\r