automatically test ini file extension for files from commandline option @filename
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 \r
107 typedef struct {\r
108   ChessSquare piece;  \r
109   POINT pos;      /* window coordinates of current pos */\r
110   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
111   POINT from;     /* board coordinates of the piece's orig pos */\r
112   POINT to;       /* board coordinates of the piece's new pos */\r
113 } AnimInfo;\r
114 \r
115 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
116 \r
117 typedef struct {\r
118   POINT start;    /* window coordinates of start pos */\r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122 } DragInfo;\r
123 \r
124 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT sq[2];    /* board coordinates of from, to squares */\r
128 } HighlightInfo;\r
129 \r
130 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
131 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
132 \r
133 /* Window class names */\r
134 char szAppName[] = "WinBoard";\r
135 char szConsoleName[] = "WBConsole";\r
136 \r
137 /* Title bar text */\r
138 char szTitle[] = "WinBoard";\r
139 char szConsoleTitle[] = "ICS Interaction";\r
140 \r
141 char *programName;\r
142 char *settingsFileName;\r
143 BOOLEAN saveSettingsOnExit;\r
144 char installDir[MSG_SIZ];\r
145 \r
146 BoardSize boardSize;\r
147 BOOLEAN chessProgram;\r
148 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
149 static int squareSize, lineGap, minorSize;\r
150 static int winWidth, winHeight;\r
151 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
152 static int logoHeight = 0;\r
153 static char messageText[MESSAGE_TEXT_MAX];\r
154 static int clockTimerEvent = 0;\r
155 static int loadGameTimerEvent = 0;\r
156 static int analysisTimerEvent = 0;\r
157 static DelayedEventCallback delayedTimerCallback;\r
158 static int delayedTimerEvent = 0;\r
159 static int buttonCount = 2;\r
160 char *icsTextMenuString;\r
161 char *icsNames;\r
162 char *firstChessProgramNames;\r
163 char *secondChessProgramNames;\r
164 \r
165 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
166 \r
167 #define PALETTESIZE 256\r
168 \r
169 HINSTANCE hInst;          /* current instance */\r
170 HWND hwndMain = NULL;        /* root window*/\r
171 HWND hwndConsole = NULL;\r
172 BOOLEAN alwaysOnTop = FALSE;\r
173 RECT boardRect;\r
174 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
175   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
176 HPALETTE hPal;\r
177 ColorClass currentColorClass;\r
178 \r
179 HWND hCommPort = NULL;    /* currently open comm port */\r
180 static HWND hwndPause;    /* pause button */\r
181 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
182 static HBRUSH lightSquareBrush, darkSquareBrush,\r
183   blackSquareBrush, /* [HGM] for band between board and holdings */\r
184   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
185 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
186 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
187 static HPEN gridPen = NULL;\r
188 static HPEN highlightPen = NULL;\r
189 static HPEN premovePen = NULL;\r
190 static NPLOGPALETTE pLogPal;\r
191 static BOOL paletteChanged = FALSE;\r
192 static HICON iconWhite, iconBlack, iconCurrent;\r
193 static int doingSizing = FALSE;\r
194 static int lastSizing = 0;\r
195 static int prevStderrPort;\r
196 \r
197 /* [AS] Support for background textures */\r
198 #define BACK_TEXTURE_MODE_DISABLED      0\r
199 #define BACK_TEXTURE_MODE_PLAIN         1\r
200 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
201 \r
202 static HBITMAP liteBackTexture = NULL;\r
203 static HBITMAP darkBackTexture = NULL;\r
204 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
205 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
206 static int backTextureSquareSize = 0;\r
207 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
208 \r
209 #if __GNUC__ && !defined(_winmajor)\r
210 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
211 #else\r
212 #define oldDialog (_winmajor < 4)\r
213 #endif\r
214 \r
215 char *defaultTextAttribs[] = \r
216 {\r
217   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
218   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
219   COLOR_NONE\r
220 };\r
221 \r
222 typedef struct {\r
223   char *name;\r
224   int squareSize;\r
225   int lineGap;\r
226   int smallLayout;\r
227   int tinyLayout;\r
228   int cliWidth, cliHeight;\r
229 } SizeInfo;\r
230 \r
231 SizeInfo sizeInfo[] = \r
232 {\r
233   { "tiny",     21, 0, 1, 1, 0, 0 },\r
234   { "teeny",    25, 1, 1, 1, 0, 0 },\r
235   { "dinky",    29, 1, 1, 1, 0, 0 },\r
236   { "petite",   33, 1, 1, 1, 0, 0 },\r
237   { "slim",     37, 2, 1, 0, 0, 0 },\r
238   { "small",    40, 2, 1, 0, 0, 0 },\r
239   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
240   { "middling", 49, 2, 0, 0, 0, 0 },\r
241   { "average",  54, 2, 0, 0, 0, 0 },\r
242   { "moderate", 58, 3, 0, 0, 0, 0 },\r
243   { "medium",   64, 3, 0, 0, 0, 0 },\r
244   { "bulky",    72, 3, 0, 0, 0, 0 },\r
245   { "large",    80, 3, 0, 0, 0, 0 },\r
246   { "big",      87, 3, 0, 0, 0, 0 },\r
247   { "huge",     95, 3, 0, 0, 0, 0 },\r
248   { "giant",    108, 3, 0, 0, 0, 0 },\r
249   { "colossal", 116, 4, 0, 0, 0, 0 },\r
250   { "titanic",  129, 4, 0, 0, 0, 0 },\r
251   { NULL, 0, 0, 0, 0, 0, 0 }\r
252 };\r
253 \r
254 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
255 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
256 {\r
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275 };\r
276 \r
277 MyFont *font[NUM_SIZES][NUM_FONTS];\r
278 \r
279 typedef struct {\r
280   char *label;\r
281   int id;\r
282   HWND hwnd;\r
283   WNDPROC wndproc;\r
284 } MyButtonDesc;\r
285 \r
286 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
287 #define N_BUTTONS 5\r
288 \r
289 MyButtonDesc buttonDesc[N_BUTTONS] =\r
290 {\r
291   {"<<", IDM_ToStart, NULL, NULL},\r
292   {"<", IDM_Backward, NULL, NULL},\r
293   {"P", IDM_Pause, NULL, NULL},\r
294   {">", IDM_Forward, NULL, NULL},\r
295   {">>", IDM_ToEnd, NULL, NULL},\r
296 };\r
297 \r
298 int tinyLayout = 0, smallLayout = 0;\r
299 #define MENU_BAR_ITEMS 6\r
300 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
301   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
302   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
303 };\r
304 \r
305 \r
306 MySound sounds[(int)NSoundClasses];\r
307 MyTextAttribs textAttribs[(int)NColorClasses];\r
308 \r
309 MyColorizeAttribs colorizeAttribs[] = {\r
310   { (COLORREF)0, 0, "Shout Text" },\r
311   { (COLORREF)0, 0, "SShout/CShout" },\r
312   { (COLORREF)0, 0, "Channel 1 Text" },\r
313   { (COLORREF)0, 0, "Channel Text" },\r
314   { (COLORREF)0, 0, "Kibitz Text" },\r
315   { (COLORREF)0, 0, "Tell Text" },\r
316   { (COLORREF)0, 0, "Challenge Text" },\r
317   { (COLORREF)0, 0, "Request Text" },\r
318   { (COLORREF)0, 0, "Seek Text" },\r
319   { (COLORREF)0, 0, "Normal Text" },\r
320   { (COLORREF)0, 0, "None" }\r
321 };\r
322 \r
323 \r
324 \r
325 static char *commentTitle;\r
326 static char *commentText;\r
327 static int commentIndex;\r
328 static Boolean editComment = FALSE;\r
329 HWND commentDialog = NULL;\r
330 BOOLEAN commentDialogUp = FALSE;\r
331 static int commentX, commentY, commentH, commentW;\r
332 \r
333 static char *analysisTitle;\r
334 static char *analysisText;\r
335 HWND analysisDialog = NULL;\r
336 BOOLEAN analysisDialogUp = FALSE;\r
337 static int analysisX, analysisY, analysisH, analysisW;\r
338 \r
339 char errorTitle[MSG_SIZ];\r
340 char errorMessage[2*MSG_SIZ];\r
341 HWND errorDialog = NULL;\r
342 BOOLEAN moveErrorMessageUp = FALSE;\r
343 BOOLEAN consoleEcho = TRUE;\r
344 CHARFORMAT consoleCF;\r
345 COLORREF consoleBackgroundColor;\r
346 \r
347 char *programVersion;\r
348 \r
349 #define CPReal 1\r
350 #define CPComm 2\r
351 #define CPSock 3\r
352 #define CPRcmd 4\r
353 typedef int CPKind;\r
354 \r
355 typedef struct {\r
356   CPKind kind;\r
357   HANDLE hProcess;\r
358   DWORD pid;\r
359   HANDLE hTo;\r
360   HANDLE hFrom;\r
361   SOCKET sock;\r
362   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
363 } ChildProc;\r
364 \r
365 #define INPUT_SOURCE_BUF_SIZE 4096\r
366 \r
367 typedef struct _InputSource {\r
368   CPKind kind;\r
369   HANDLE hFile;\r
370   SOCKET sock;\r
371   int lineByLine;\r
372   HANDLE hThread;\r
373   DWORD id;\r
374   char buf[INPUT_SOURCE_BUF_SIZE];\r
375   char *next;\r
376   DWORD count;\r
377   int error;\r
378   InputCallback func;\r
379   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
380   VOIDSTAR closure;\r
381 } InputSource;\r
382 \r
383 InputSource *consoleInputSource;\r
384 \r
385 DCB dcb;\r
386 \r
387 /* forward */\r
388 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
389 VOID ConsoleCreate();\r
390 LRESULT CALLBACK\r
391   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
392 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
393 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
394 VOID ParseCommSettings(char *arg, DCB *dcb);\r
395 LRESULT CALLBACK\r
396   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
397 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
398 void ParseIcsTextMenu(char *icsTextMenuString);\r
399 VOID PopUpMoveDialog(char firstchar);\r
400 VOID PopUpNameDialog(char firstchar);\r
401 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
402 \r
403 /* [AS] */\r
404 int NewGameFRC();\r
405 int GameListOptions();\r
406 \r
407 HWND moveHistoryDialog = NULL;\r
408 BOOLEAN moveHistoryDialogUp = FALSE;\r
409 \r
410 WindowPlacement wpMoveHistory;\r
411 \r
412 HWND evalGraphDialog = NULL;\r
413 BOOLEAN evalGraphDialogUp = FALSE;\r
414 \r
415 WindowPlacement wpEvalGraph;\r
416 \r
417 HWND engineOutputDialog = NULL;\r
418 BOOLEAN engineOutputDialogUp = FALSE;\r
419 \r
420 WindowPlacement wpEngineOutput;\r
421 \r
422 VOID MoveHistoryPopUp();\r
423 VOID MoveHistoryPopDown();\r
424 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
425 BOOL MoveHistoryIsUp();\r
426 \r
427 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
428 VOID EvalGraphPopUp();\r
429 VOID EvalGraphPopDown();\r
430 BOOL EvalGraphIsUp();\r
431 \r
432 VOID EngineOutputPopUp();\r
433 VOID EngineOutputPopDown();\r
434 BOOL EngineOutputIsUp();\r
435 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
436 \r
437 VOID GothicPopUp(char *title, VariantClass variant);\r
438 /*\r
439  * Setting "frozen" should disable all user input other than deleting\r
440  * the window.  We do this while engines are initializing themselves.\r
441  */\r
442 static int frozen = 0;\r
443 static int oldMenuItemState[MENU_BAR_ITEMS];\r
444 void FreezeUI()\r
445 {\r
446   HMENU hmenu;\r
447   int i;\r
448 \r
449   if (frozen) return;\r
450   frozen = 1;\r
451   hmenu = GetMenu(hwndMain);\r
452   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
453     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
454   }\r
455   DrawMenuBar(hwndMain);\r
456 }\r
457 \r
458 /* Undo a FreezeUI */\r
459 void ThawUI()\r
460 {\r
461   HMENU hmenu;\r
462   int i;\r
463 \r
464   if (!frozen) return;\r
465   frozen = 0;\r
466   hmenu = GetMenu(hwndMain);\r
467   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
468     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
469   }\r
470   DrawMenuBar(hwndMain);\r
471 }\r
472 \r
473 /*---------------------------------------------------------------------------*\\r
474  *\r
475  * WinMain\r
476  *\r
477 \*---------------------------------------------------------------------------*/\r
478 \r
479 int APIENTRY\r
480 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
481         LPSTR lpCmdLine, int nCmdShow)\r
482 {\r
483   MSG msg;\r
484   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
485 //  INITCOMMONCONTROLSEX ex;\r
486 \r
487   debugFP = stderr;\r
488 \r
489   LoadLibrary("RICHED32.DLL");\r
490   consoleCF.cbSize = sizeof(CHARFORMAT);\r
491 \r
492   if (!InitApplication(hInstance)) {\r
493     return (FALSE);\r
494   }\r
495   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
496     return (FALSE);\r
497   }\r
498 \r
499 //  InitCommonControlsEx(&ex);\r
500   InitCommonControls();\r
501 \r
502   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
503   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
504   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
505 \r
506   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
507 \r
508   while (GetMessage(&msg, /* message structure */\r
509                     NULL, /* handle of window receiving the message */\r
510                     0,    /* lowest message to examine */\r
511                     0))   /* highest message to examine */\r
512     {\r
513       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
514           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
515           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
516           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
517           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
518           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
519           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
520           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
521           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
522           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
523         TranslateMessage(&msg); /* Translates virtual key codes */\r
524         DispatchMessage(&msg);  /* Dispatches message to window */\r
525       }\r
526     }\r
527 \r
528 \r
529   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
530 }\r
531 \r
532 /*---------------------------------------------------------------------------*\\r
533  *\r
534  * Initialization functions\r
535  *\r
536 \*---------------------------------------------------------------------------*/\r
537 \r
538 BOOL\r
539 InitApplication(HINSTANCE hInstance)\r
540 {\r
541   WNDCLASS wc;\r
542 \r
543   /* Fill in window class structure with parameters that describe the */\r
544   /* main window. */\r
545 \r
546   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
547   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
548   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
549   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
550   wc.hInstance     = hInstance;         /* Owner of this class */\r
551   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
552   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
553   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
554   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
555   wc.lpszClassName = szAppName;                 /* Name to register as */\r
556 \r
557   /* Register the window class and return success/failure code. */\r
558   if (!RegisterClass(&wc)) return FALSE;\r
559 \r
560   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
561   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
562   wc.cbClsExtra    = 0;\r
563   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
564   wc.hInstance     = hInstance;\r
565   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
566   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
567   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
568   wc.lpszMenuName  = NULL;\r
569   wc.lpszClassName = szConsoleName;\r
570 \r
571   if (!RegisterClass(&wc)) return FALSE;\r
572   return TRUE;\r
573 }\r
574 \r
575 \r
576 /* Set by InitInstance, used by EnsureOnScreen */\r
577 int screenHeight, screenWidth;\r
578 \r
579 void\r
580 EnsureOnScreen(int *x, int *y)\r
581 {\r
582 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
583   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
584   if (*x > screenWidth - 32) *x = 0;\r
585   if (*y > screenHeight - 32) *y = 0;\r
586   if (*x < 0) *x = 0;\r
587   if (*y < 0) *y = 0;\r
588 //  if (*x < 10) *x = 10;\r
589 //  if (*y < gap) *y = gap;\r
590 }\r
591 \r
592 BOOL\r
593 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
594 {\r
595   HWND hwnd; /* Main window handle. */\r
596   int ibs;\r
597   WINDOWPLACEMENT wp;\r
598   char *filepart;\r
599 \r
600   hInst = hInstance;    /* Store instance handle in our global variable */\r
601 \r
602   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
603     *filepart = NULLCHAR;\r
604   } else {\r
605     GetCurrentDirectory(MSG_SIZ, installDir);\r
606   }\r
607   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
608   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
609   if (appData.debugMode) {\r
610     debugFP = fopen(appData.nameOfDebugFile, "w");\r
611     setbuf(debugFP, NULL);\r
612   }\r
613 \r
614   InitBackEnd1();\r
615 \r
616 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
617 //  InitEngineUCI( installDir, &second );\r
618 \r
619   /* Create a main window for this application instance. */\r
620   hwnd = CreateWindow(szAppName, szTitle,\r
621                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
622                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
623                       NULL, NULL, hInstance, NULL);\r
624   hwndMain = hwnd;\r
625 \r
626   /* If window could not be created, return "failure" */\r
627   if (!hwnd) {\r
628     return (FALSE);\r
629   }\r
630 \r
631   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
632   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
633       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
634 \r
635       if (first.programLogo == NULL && appData.debugMode) {\r
636           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
637       }\r
638   } else if(appData.autoLogo) {\r
639       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
640         char buf[MSG_SIZ];\r
641         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
642         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
643       }\r
644   }\r
645 \r
646   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
647       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
648 \r
649       if (second.programLogo == NULL && appData.debugMode) {\r
650           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
651       }\r
652   } else if(appData.autoLogo) {\r
653       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
654         char buf[MSG_SIZ];\r
655         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
656         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
657       }\r
658   }\r
659 \r
660   iconWhite = LoadIcon(hInstance, "icon_white");\r
661   iconBlack = LoadIcon(hInstance, "icon_black");\r
662   iconCurrent = iconWhite;\r
663   InitDrawingColors();\r
664   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
665   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
666   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
667     /* Compute window size for each board size, and use the largest\r
668        size that fits on this screen as the default. */\r
669     InitDrawingSizes((BoardSize)ibs, 0);\r
670     if (boardSize == (BoardSize)-1 &&\r
671         winHeight <= screenHeight\r
672            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
673         && winWidth <= screenWidth) {\r
674       boardSize = (BoardSize)ibs;\r
675     }\r
676   }\r
677 \r
678   InitDrawingSizes(boardSize, 0);\r
679   InitMenuChecks();\r
680   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
681 \r
682   /* [AS] Load textures if specified */\r
683   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
684   \r
685   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
686       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
687       liteBackTextureMode = appData.liteBackTextureMode;\r
688 \r
689       if (liteBackTexture == NULL && appData.debugMode) {\r
690           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
691       }\r
692   }\r
693   \r
694   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
695       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
696       darkBackTextureMode = appData.darkBackTextureMode;\r
697 \r
698       if (darkBackTexture == NULL && appData.debugMode) {\r
699           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
700       }\r
701   }\r
702 \r
703   mysrandom( (unsigned) time(NULL) );\r
704 \r
705   /* [AS] Restore layout */\r
706   if( wpMoveHistory.visible ) {\r
707       MoveHistoryPopUp();\r
708   }\r
709 \r
710   if( wpEvalGraph.visible ) {\r
711       EvalGraphPopUp();\r
712   }\r
713 \r
714   if( wpEngineOutput.visible ) {\r
715       EngineOutputPopUp();\r
716   }\r
717 \r
718   InitBackEnd2();\r
719 \r
720   /* Make the window visible; update its client area; and return "success" */\r
721   EnsureOnScreen(&boardX, &boardY);\r
722   wp.length = sizeof(WINDOWPLACEMENT);\r
723   wp.flags = 0;\r
724   wp.showCmd = nCmdShow;\r
725   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
726   wp.rcNormalPosition.left = boardX;\r
727   wp.rcNormalPosition.right = boardX + winWidth;\r
728   wp.rcNormalPosition.top = boardY;\r
729   wp.rcNormalPosition.bottom = boardY + winHeight;\r
730   SetWindowPlacement(hwndMain, &wp);\r
731 \r
732   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
733                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
734 \r
735 #if 0\r
736   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
737   if( gameInfo.variant != VariantFischeRandom ) {\r
738       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
739   }\r
740 #endif\r
741   if (hwndConsole) {\r
742 #if AOT_CONSOLE\r
743     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
744                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
745 #endif\r
746     ShowWindow(hwndConsole, nCmdShow);\r
747   }\r
748   UpdateWindow(hwnd);\r
749 \r
750   return TRUE;\r
751 \r
752 }\r
753 \r
754 \r
755 typedef enum {\r
756   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
757   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
758   ArgSettingsFilename\r
759 } ArgType;\r
760 \r
761 typedef struct {\r
762   char *argName;\r
763   ArgType argType;\r
764   /***\r
765   union {\r
766     String *pString;       // ArgString\r
767     int *pInt;             // ArgInt\r
768     float *pFloat;         // ArgFloat\r
769     Boolean *pBoolean;     // ArgBoolean\r
770     COLORREF *pColor;      // ArgColor\r
771     ColorClass cc;         // ArgAttribs\r
772     String *pFilename;     // ArgFilename\r
773     BoardSize *pBoardSize; // ArgBoardSize\r
774     int whichFont;         // ArgFont\r
775     DCB *pDCB;             // ArgCommSettings\r
776     String *pFilename;     // ArgSettingsFilename\r
777   } argLoc;\r
778   ***/\r
779   LPVOID argLoc;\r
780   BOOL save;\r
781 } ArgDescriptor;\r
782 \r
783 int junk;\r
784 ArgDescriptor argDescriptors[] = {\r
785   /* positional arguments */\r
786   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
787   { "", ArgNone, NULL },\r
788   /* keyword arguments */\r
789   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
790   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
791   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
792   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
793   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
794   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
795   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
796   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
797   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
798   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
799   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
800   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
801   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
802   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
803   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
804   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
805   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
806   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
807     FALSE },\r
808   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
809     FALSE },\r
810   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
811     FALSE },\r
812   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
813   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
814     FALSE },\r
815   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
816   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
817   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
818   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
819   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
820   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
821   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
822   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
823   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
824   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
825   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
826   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
827   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
828   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
829   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
830   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
831   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
832   /*!!bitmapDirectory?*/\r
833   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
834   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
835   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
836   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
837   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
838   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
839   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
840   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
841   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
842   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
843   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
844   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
845   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
846   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
847   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
848   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
849   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
850   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
851   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
852   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
853   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
854   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
855   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
856   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
857   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
858   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
859   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
860   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
861   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
862   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
863   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
864   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
865   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
866   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
867   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
868   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
869   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
870   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
871   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
872   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
873   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
874   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
875   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
876   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
877   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
878   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
879   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
880   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
881   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
882   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
883   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
884   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
885   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
886   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
887   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
888   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
889   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
890   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
891   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
892   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
893   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
894   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
895   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
896   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
897   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
898   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
899   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
900   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
901   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
902   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
903   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
904   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
905   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
906   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
907   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
908   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
909   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
910   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
911   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
912   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
913   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
914   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
915   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
916   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
917   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
918   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
919   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
920   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
921   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
922   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
923   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
924   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
925   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
926   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
927     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
928   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
929   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
930   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
931   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
932   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
933   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
934   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
935   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
936     TRUE }, /* must come after all fonts */\r
937   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
938   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
939     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
940   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
941   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
942   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
943   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
944   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
945   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
946   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
947   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
948   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
949   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
950   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
951   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
952   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
953   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
954   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
955   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
956   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
957   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
958   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
959   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
960   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
961   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
962   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
963   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
964   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
965   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
966   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
967   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
968 #if 0\r
969   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
970   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
971 #endif\r
972   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
973   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
974   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
975   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
976   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
977   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
978   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
979   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
980   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
981   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
982   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
983   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
984   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
985   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
986   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
987   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
988   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
989   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
990   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
991   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
992   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
993   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
994   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
995   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
996   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
997   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
998   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
999   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1000   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1001   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1002   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1003   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1004   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1005   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1006   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1007   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1008   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1009   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1010   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1011   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1012   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1013   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1014   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1015   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1016   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1017   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1018   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1019   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1020   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1021   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1022   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1023   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1024   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1025   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1026   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1027   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1028   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1029   { "highlightLastMove", ArgBoolean,\r
1030     (LPVOID) &appData.highlightLastMove, TRUE },\r
1031   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1032   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1033   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1034   { "highlightDragging", ArgBoolean,\r
1035     (LPVOID) &appData.highlightDragging, TRUE },\r
1036   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1037   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1038   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1039   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1040   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1041   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1042   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1043   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1044   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1045   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1046   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1047   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1048   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1049   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1050   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1051   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1052   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1053   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1054   { "soundShout", ArgFilename,\r
1055     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1056   { "soundSShout", ArgFilename,\r
1057     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1058   { "soundChannel1", ArgFilename,\r
1059     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1060   { "soundChannel", ArgFilename,\r
1061     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1062   { "soundKibitz", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1064   { "soundTell", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1066   { "soundChallenge", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1068   { "soundRequest", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1070   { "soundSeek", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1072   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1073   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1074   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1075   { "soundIcsLoss", ArgFilename, \r
1076     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1077   { "soundIcsDraw", ArgFilename, \r
1078     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1079   { "soundIcsUnfinished", ArgFilename, \r
1080     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1081   { "soundIcsAlarm", ArgFilename, \r
1082     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1083   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1084   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1085   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1086   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1087   { "reuseChessPrograms", ArgBoolean,\r
1088     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1089   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1090   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1091   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1092   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1093   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1094   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1095   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1096   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1097   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1098   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1099   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1100   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1101   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1102   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1103   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1104   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1105   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1106   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1107   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1108   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1109   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1110   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1111   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1112   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1113   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1114   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1115   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1116   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1117   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1118   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1119   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1120   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1121   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1122   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1123   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1124   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1125   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1126     TRUE },\r
1127   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1128     TRUE },\r
1129   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1130   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1131   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1132   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1133   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1134   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1135   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1136   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1137   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1138   /* [AS] New features */\r
1139   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1140   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1141   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1142   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1143   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1144   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1145   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1146   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1147   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1148   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1149   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1150   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1151   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1152   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1153   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1154   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1155   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1156   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1157   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1158   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1159   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1160   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1161   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1162   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1163   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1164   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1165   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1166   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1167   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1168   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1169   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1170   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1171   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1172   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1173   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1174   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1175   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1176   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1177   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1178   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1179   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1180   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1181   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1182   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1183   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1184   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1185   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1186   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1187   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1188   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1189 \r
1190   /* [AS] Layout stuff */\r
1191   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1192   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1193   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1194   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1195   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1196 \r
1197   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1198   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1199   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1200   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1201   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1202 \r
1203   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1204   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1205   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1206   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1207   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1208 \r
1209   /* [HGM] board-size, adjudication and misc. options */\r
1210   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1211   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1212   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1213   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1214   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1215   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1216   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1217   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1218   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1219   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1220   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1221   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1222   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1223   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1224   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1225   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1226   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1227   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1228   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1229   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1230   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1231   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1232   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1233   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1234   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1235   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1236   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1237   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1238   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1239 \r
1240 #ifdef ZIPPY\r
1241   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1242   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1243   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1244   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1245   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1246   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1247   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1248   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1249   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1250   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1251   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1252   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1253   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1254     FALSE },\r
1255   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1256   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1257   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1258   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1259   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1260   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1261   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1262     FALSE },\r
1263   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1264   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1265   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1266   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1267   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1268   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1269   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1270   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1271   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1272   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1273   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1274   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1275   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1276   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1277   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1278   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1279   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1280   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1281 #endif\r
1282   /* [HGM] options for broadcasting and time odds */\r
1283   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1284   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1285   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1286   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1287   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1288   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1289   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1290   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1291   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1292   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1293   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1294   { NULL, ArgNone, NULL, FALSE }\r
1295 };\r
1296 \r
1297 \r
1298 /* Kludge for indirection files on command line */\r
1299 char* lastIndirectionFilename;\r
1300 ArgDescriptor argDescriptorIndirection =\r
1301 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1302 \r
1303 \r
1304 VOID\r
1305 ExitArgError(char *msg, char *badArg)\r
1306 {\r
1307   char buf[MSG_SIZ];\r
1308 \r
1309   sprintf(buf, "%s %s", msg, badArg);\r
1310   DisplayFatalError(buf, 0, 2);\r
1311   exit(2);\r
1312 }\r
1313 \r
1314 /* Command line font name parser.  NULL name means do nothing.\r
1315    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1316    For backward compatibility, syntax without the colon is also\r
1317    accepted, but font names with digits in them won't work in that case.\r
1318 */\r
1319 VOID\r
1320 ParseFontName(char *name, MyFontParams *mfp)\r
1321 {\r
1322   char *p, *q;\r
1323   if (name == NULL) return;\r
1324   p = name;\r
1325   q = strchr(p, ':');\r
1326   if (q) {\r
1327     if (q - p >= sizeof(mfp->faceName))\r
1328       ExitArgError("Font name too long:", name);\r
1329     memcpy(mfp->faceName, p, q - p);\r
1330     mfp->faceName[q - p] = NULLCHAR;\r
1331     p = q + 1;\r
1332   } else {\r
1333     q = mfp->faceName;\r
1334     while (*p && !isdigit(*p)) {\r
1335       *q++ = *p++;\r
1336       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1337         ExitArgError("Font name too long:", name);\r
1338     }\r
1339     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1340     *q = NULLCHAR;\r
1341   }\r
1342   if (!*p) ExitArgError("Font point size missing:", name);\r
1343   mfp->pointSize = (float) atof(p);\r
1344   mfp->bold = (strchr(p, 'b') != NULL);\r
1345   mfp->italic = (strchr(p, 'i') != NULL);\r
1346   mfp->underline = (strchr(p, 'u') != NULL);\r
1347   mfp->strikeout = (strchr(p, 's') != NULL);\r
1348 }\r
1349 \r
1350 /* Color name parser.\r
1351    X version accepts X color names, but this one\r
1352    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1353 COLORREF\r
1354 ParseColorName(char *name)\r
1355 {\r
1356   int red, green, blue, count;\r
1357   char buf[MSG_SIZ];\r
1358 \r
1359   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1360   if (count != 3) {\r
1361     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1362       &red, &green, &blue);\r
1363   }\r
1364   if (count != 3) {\r
1365     sprintf(buf, "Can't parse color name %s", name);\r
1366     DisplayError(buf, 0);\r
1367     return RGB(0, 0, 0);\r
1368   }\r
1369   return PALETTERGB(red, green, blue);\r
1370 }\r
1371 \r
1372 \r
1373 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1374 {\r
1375   char *e = argValue;\r
1376   int eff = 0;\r
1377 \r
1378   while (*e) {\r
1379     if (*e == 'b')      eff |= CFE_BOLD;\r
1380     else if (*e == 'i') eff |= CFE_ITALIC;\r
1381     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1382     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1383     else if (*e == '#' || isdigit(*e)) break;\r
1384     e++;\r
1385   }\r
1386   *effects = eff;\r
1387   *color   = ParseColorName(e);\r
1388 }\r
1389 \r
1390 \r
1391 BoardSize\r
1392 ParseBoardSize(char *name)\r
1393 {\r
1394   BoardSize bs = SizeTiny;\r
1395   while (sizeInfo[bs].name != NULL) {\r
1396     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1397     bs++;\r
1398   }\r
1399   ExitArgError("Unrecognized board size value", name);\r
1400   return bs; /* not reached */\r
1401 }\r
1402 \r
1403 \r
1404 char\r
1405 StringGet(void *getClosure)\r
1406 {\r
1407   char **p = (char **) getClosure;\r
1408   return *((*p)++);\r
1409 }\r
1410 \r
1411 char\r
1412 FileGet(void *getClosure)\r
1413 {\r
1414   int c;\r
1415   FILE* f = (FILE*) getClosure;\r
1416 \r
1417   c = getc(f);\r
1418   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1419   if (c == EOF)\r
1420     return NULLCHAR;\r
1421   else\r
1422     return (char) c;\r
1423 }\r
1424 \r
1425 /* Parse settings file named "name". If file found, return the\r
1426    full name in fullname and return TRUE; else return FALSE */\r
1427 BOOLEAN\r
1428 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1429 {\r
1430   char *dummy;\r
1431   FILE *f;\r
1432   int ok; char buf[MSG_SIZ];\r
1433 \r
1434   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1435   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1436     sprintf(buf, "%s.ini", name);\r
1437     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1438   }\r
1439   if (ok) {\r
1440     f = fopen(fullname, "r");\r
1441     if (f != NULL) {\r
1442       ParseArgs(FileGet, f);\r
1443       fclose(f);\r
1444       return TRUE;\r
1445     }\r
1446   }\r
1447   return FALSE;\r
1448 }\r
1449 \r
1450 VOID\r
1451 ParseArgs(GetFunc get, void *cl)\r
1452 {\r
1453   char argName[ARG_MAX];\r
1454   char argValue[ARG_MAX];\r
1455   ArgDescriptor *ad;\r
1456   char start;\r
1457   char *q;\r
1458   int i, octval;\r
1459   char ch;\r
1460   int posarg = 0;\r
1461 \r
1462   ch = get(cl);\r
1463   for (;;) {\r
1464     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1465     if (ch == NULLCHAR) break;\r
1466     if (ch == ';') {\r
1467       /* Comment to end of line */\r
1468       ch = get(cl);\r
1469       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1470       continue;\r
1471     } else if (ch == '/' || ch == '-') {\r
1472       /* Switch */\r
1473       q = argName;\r
1474       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1475              ch != '\n' && ch != '\t') {\r
1476         *q++ = ch;\r
1477         ch = get(cl);\r
1478       }\r
1479       *q = NULLCHAR;\r
1480 \r
1481       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1482         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1483 \r
1484       if (ad->argName == NULL)\r
1485         ExitArgError("Unrecognized argument", argName);\r
1486 \r
1487     } else if (ch == '@') {\r
1488       /* Indirection file */\r
1489       ad = &argDescriptorIndirection;\r
1490       ch = get(cl);\r
1491     } else {\r
1492       /* Positional argument */\r
1493       ad = &argDescriptors[posarg++];\r
1494       strcpy(argName, ad->argName);\r
1495     }\r
1496 \r
1497     if (ad->argType == ArgTrue) {\r
1498       *(Boolean *) ad->argLoc = TRUE;\r
1499       continue;\r
1500     }\r
1501     if (ad->argType == ArgFalse) {\r
1502       *(Boolean *) ad->argLoc = FALSE;\r
1503       continue;\r
1504     }\r
1505 \r
1506     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1507     if (ch == NULLCHAR || ch == '\n') {\r
1508       ExitArgError("No value provided for argument", argName);\r
1509     }\r
1510     q = argValue;\r
1511     if (ch == '{') {\r
1512       // Quoting with { }.  No characters have to (or can) be escaped.\r
1513       // Thus the string cannot contain a '}' character.\r
1514       start = ch;\r
1515       ch = get(cl);\r
1516       while (start) {\r
1517         switch (ch) {\r
1518         case NULLCHAR:\r
1519           start = NULLCHAR;\r
1520           break;\r
1521           \r
1522         case '}':\r
1523           ch = get(cl);\r
1524           start = NULLCHAR;\r
1525           break;\r
1526 \r
1527         default:\r
1528           *q++ = ch;\r
1529           ch = get(cl);\r
1530           break;\r
1531         }\r
1532       }   \r
1533     } else if (ch == '\'' || ch == '"') {\r
1534       // Quoting with ' ' or " ", with \ as escape character.\r
1535       // Inconvenient for long strings that may contain Windows filenames.\r
1536       start = ch;\r
1537       ch = get(cl);\r
1538       while (start) {\r
1539         switch (ch) {\r
1540         case NULLCHAR:\r
1541           start = NULLCHAR;\r
1542           break;\r
1543 \r
1544         default:\r
1545         not_special:\r
1546           *q++ = ch;\r
1547           ch = get(cl);\r
1548           break;\r
1549 \r
1550         case '\'':\r
1551         case '\"':\r
1552           if (ch == start) {\r
1553             ch = get(cl);\r
1554             start = NULLCHAR;\r
1555             break;\r
1556           } else {\r
1557             goto not_special;\r
1558           }\r
1559 \r
1560         case '\\':\r
1561           if (ad->argType == ArgFilename\r
1562               || ad->argType == ArgSettingsFilename) {\r
1563               goto not_special;\r
1564           }\r
1565           ch = get(cl);\r
1566           switch (ch) {\r
1567           case NULLCHAR:\r
1568             ExitArgError("Incomplete \\ escape in value for", argName);\r
1569             break;\r
1570           case 'n':\r
1571             *q++ = '\n';\r
1572             ch = get(cl);\r
1573             break;\r
1574           case 'r':\r
1575             *q++ = '\r';\r
1576             ch = get(cl);\r
1577             break;\r
1578           case 't':\r
1579             *q++ = '\t';\r
1580             ch = get(cl);\r
1581             break;\r
1582           case 'b':\r
1583             *q++ = '\b';\r
1584             ch = get(cl);\r
1585             break;\r
1586           case 'f':\r
1587             *q++ = '\f';\r
1588             ch = get(cl);\r
1589             break;\r
1590           default:\r
1591             octval = 0;\r
1592             for (i = 0; i < 3; i++) {\r
1593               if (ch >= '0' && ch <= '7') {\r
1594                 octval = octval*8 + (ch - '0');\r
1595                 ch = get(cl);\r
1596               } else {\r
1597                 break;\r
1598               }\r
1599             }\r
1600             if (i > 0) {\r
1601               *q++ = (char) octval;\r
1602             } else {\r
1603               *q++ = ch;\r
1604               ch = get(cl);\r
1605             }\r
1606             break;\r
1607           }\r
1608           break;\r
1609         }\r
1610       }\r
1611     } else {\r
1612       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1613         *q++ = ch;\r
1614         ch = get(cl);\r
1615       }\r
1616     }\r
1617     *q = NULLCHAR;\r
1618 \r
1619     switch (ad->argType) {\r
1620     case ArgInt:\r
1621       *(int *) ad->argLoc = atoi(argValue);\r
1622       break;\r
1623 \r
1624     case ArgFloat:\r
1625       *(float *) ad->argLoc = (float) atof(argValue);\r
1626       break;\r
1627 \r
1628     case ArgString:\r
1629     case ArgFilename:\r
1630       *(char **) ad->argLoc = strdup(argValue);\r
1631       break;\r
1632 \r
1633     case ArgSettingsFilename:\r
1634       {\r
1635         char fullname[MSG_SIZ];\r
1636         if (ParseSettingsFile(argValue, fullname)) {\r
1637           if (ad->argLoc != NULL) {\r
1638             *(char **) ad->argLoc = strdup(fullname);\r
1639           }\r
1640         } else {\r
1641           if (ad->argLoc != NULL) {\r
1642           } else {\r
1643             ExitArgError("Failed to open indirection file", argValue);\r
1644           }\r
1645         }\r
1646       }\r
1647       break;\r
1648 \r
1649     case ArgBoolean:\r
1650       switch (argValue[0]) {\r
1651       case 't':\r
1652       case 'T':\r
1653         *(Boolean *) ad->argLoc = TRUE;\r
1654         break;\r
1655       case 'f':\r
1656       case 'F':\r
1657         *(Boolean *) ad->argLoc = FALSE;\r
1658         break;\r
1659       default:\r
1660         ExitArgError("Unrecognized boolean argument value", argValue);\r
1661         break;\r
1662       }\r
1663       break;\r
1664 \r
1665     case ArgColor:\r
1666       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1667       break;\r
1668 \r
1669     case ArgAttribs: {\r
1670       ColorClass cc = (ColorClass)ad->argLoc;\r
1671       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1672       }\r
1673       break;\r
1674       \r
1675     case ArgBoardSize:\r
1676       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1677       break;\r
1678 \r
1679     case ArgFont:\r
1680       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1681       break;\r
1682 \r
1683     case ArgCommSettings:\r
1684       ParseCommSettings(argValue, &dcb);\r
1685       break;\r
1686 \r
1687     case ArgNone:\r
1688       ExitArgError("Unrecognized argument", argValue);\r
1689       break;\r
1690     case ArgTrue:\r
1691     case ArgFalse: ;\r
1692     }\r
1693   }\r
1694 }\r
1695 \r
1696 VOID\r
1697 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1698 {\r
1699   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1700   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1701   DeleteDC(hdc);\r
1702   lf->lfWidth = 0;\r
1703   lf->lfEscapement = 0;\r
1704   lf->lfOrientation = 0;\r
1705   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1706   lf->lfItalic = mfp->italic;\r
1707   lf->lfUnderline = mfp->underline;\r
1708   lf->lfStrikeOut = mfp->strikeout;\r
1709   lf->lfCharSet = DEFAULT_CHARSET;\r
1710   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1711   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1712   lf->lfQuality = DEFAULT_QUALITY;\r
1713   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1714   strcpy(lf->lfFaceName, mfp->faceName);\r
1715 }\r
1716 \r
1717 VOID\r
1718 CreateFontInMF(MyFont *mf)\r
1719 {\r
1720   LFfromMFP(&mf->lf, &mf->mfp);\r
1721   if (mf->hf) DeleteObject(mf->hf);\r
1722   mf->hf = CreateFontIndirect(&mf->lf);\r
1723 }\r
1724 \r
1725 VOID\r
1726 SetDefaultTextAttribs()\r
1727 {\r
1728   ColorClass cc;\r
1729   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1730     ParseAttribs(&textAttribs[cc].color, \r
1731                  &textAttribs[cc].effects, \r
1732                  defaultTextAttribs[cc]);\r
1733   }\r
1734 }\r
1735 \r
1736 VOID\r
1737 SetDefaultSounds()\r
1738 {\r
1739   ColorClass cc;\r
1740   SoundClass sc;\r
1741   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1742     textAttribs[cc].sound.name = strdup("");\r
1743     textAttribs[cc].sound.data = NULL;\r
1744   }\r
1745   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1746     sounds[sc].name = strdup("");\r
1747     sounds[sc].data = NULL;\r
1748   }\r
1749   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1750 }\r
1751 \r
1752 VOID\r
1753 LoadAllSounds()\r
1754 {\r
1755   ColorClass cc;\r
1756   SoundClass sc;\r
1757   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1758     MyLoadSound(&textAttribs[cc].sound);\r
1759   }\r
1760   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1761     MyLoadSound(&sounds[sc]);\r
1762   }\r
1763 }\r
1764 \r
1765 VOID\r
1766 InitAppData(LPSTR lpCmdLine)\r
1767 {\r
1768   int i, j;\r
1769   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1770   char *dummy, *p;\r
1771 \r
1772   programName = szAppName;\r
1773 \r
1774   /* Initialize to defaults */\r
1775   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1776   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1777   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1778   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1779   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1780   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1781   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1782   SetDefaultTextAttribs();\r
1783   SetDefaultSounds();\r
1784   appData.movesPerSession = MOVES_PER_SESSION;\r
1785   appData.initString = INIT_STRING;\r
1786   appData.secondInitString = INIT_STRING;\r
1787   appData.firstComputerString = COMPUTER_STRING;\r
1788   appData.secondComputerString = COMPUTER_STRING;\r
1789   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1790   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1791   appData.firstPlaysBlack = FALSE;\r
1792   appData.noChessProgram = FALSE;\r
1793   chessProgram = FALSE;\r
1794   appData.firstHost = FIRST_HOST;\r
1795   appData.secondHost = SECOND_HOST;\r
1796   appData.firstDirectory = FIRST_DIRECTORY;\r
1797   appData.secondDirectory = SECOND_DIRECTORY;\r
1798   appData.bitmapDirectory = "";\r
1799   appData.remoteShell = REMOTE_SHELL;\r
1800   appData.remoteUser = "";\r
1801   appData.timeDelay = TIME_DELAY;\r
1802   appData.timeControl = TIME_CONTROL;\r
1803   appData.timeIncrement = TIME_INCREMENT;\r
1804   appData.icsActive = FALSE;\r
1805   appData.icsHost = "";\r
1806   appData.icsPort = ICS_PORT;\r
1807   appData.icsCommPort = ICS_COMM_PORT;\r
1808   appData.icsLogon = ICS_LOGON;\r
1809   appData.icsHelper = "";\r
1810   appData.useTelnet = FALSE;\r
1811   appData.telnetProgram = TELNET_PROGRAM;\r
1812   appData.gateway = "";\r
1813   appData.loadGameFile = "";\r
1814   appData.loadGameIndex = 0;\r
1815   appData.saveGameFile = "";\r
1816   appData.autoSaveGames = FALSE;\r
1817   appData.loadPositionFile = "";\r
1818   appData.loadPositionIndex = 1;\r
1819   appData.savePositionFile = "";\r
1820   appData.matchMode = FALSE;\r
1821   appData.matchGames = 0;\r
1822   appData.monoMode = FALSE;\r
1823   appData.debugMode = FALSE;\r
1824   appData.clockMode = TRUE;\r
1825   boardSize = (BoardSize) -1; /* determine by screen size */\r
1826   appData.Iconic = FALSE; /*unused*/\r
1827   appData.searchTime = "";\r
1828   appData.searchDepth = 0;\r
1829   appData.showCoords = FALSE;\r
1830   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1831   appData.autoCallFlag = FALSE;\r
1832   appData.flipView = FALSE;\r
1833   appData.autoFlipView = TRUE;\r
1834   appData.cmailGameName = "";\r
1835   appData.alwaysPromoteToQueen = FALSE;\r
1836   appData.oldSaveStyle = FALSE;\r
1837   appData.quietPlay = FALSE;\r
1838   appData.showThinking = FALSE;\r
1839   appData.ponderNextMove = TRUE;\r
1840   appData.periodicUpdates = TRUE;\r
1841   appData.popupExitMessage = TRUE;\r
1842   appData.popupMoveErrors = FALSE;\r
1843   appData.autoObserve = FALSE;\r
1844   appData.autoComment = FALSE;\r
1845   appData.animate = TRUE;\r
1846   appData.animSpeed = 10;\r
1847   appData.animateDragging = TRUE;\r
1848   appData.highlightLastMove = TRUE;\r
1849   appData.getMoveList = TRUE;\r
1850   appData.testLegality = TRUE;\r
1851   appData.premove = TRUE;\r
1852   appData.premoveWhite = FALSE;\r
1853   appData.premoveWhiteText = "";\r
1854   appData.premoveBlack = FALSE;\r
1855   appData.premoveBlackText = "";\r
1856   appData.icsAlarm = TRUE;\r
1857   appData.icsAlarmTime = 5000;\r
1858   appData.autoRaiseBoard = TRUE;\r
1859   appData.localLineEditing = TRUE;\r
1860   appData.colorize = TRUE;\r
1861   appData.reuseFirst = TRUE;\r
1862   appData.reuseSecond = TRUE;\r
1863   appData.blindfold = FALSE;\r
1864   appData.icsEngineAnalyze = FALSE;\r
1865   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1866   dcb.DCBlength = sizeof(DCB);\r
1867   dcb.BaudRate = 9600;\r
1868   dcb.fBinary = TRUE;\r
1869   dcb.fParity = FALSE;\r
1870   dcb.fOutxCtsFlow = FALSE;\r
1871   dcb.fOutxDsrFlow = FALSE;\r
1872   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1873   dcb.fDsrSensitivity = FALSE;\r
1874   dcb.fTXContinueOnXoff = TRUE;\r
1875   dcb.fOutX = FALSE;\r
1876   dcb.fInX = FALSE;\r
1877   dcb.fNull = FALSE;\r
1878   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1879   dcb.fAbortOnError = FALSE;\r
1880   dcb.ByteSize = 7;\r
1881   dcb.Parity = SPACEPARITY;\r
1882   dcb.StopBits = ONESTOPBIT;\r
1883   settingsFileName = SETTINGS_FILE;\r
1884   saveSettingsOnExit = TRUE;\r
1885   boardX = CW_USEDEFAULT;\r
1886   boardY = CW_USEDEFAULT;\r
1887   consoleX = CW_USEDEFAULT; \r
1888   consoleY = CW_USEDEFAULT; \r
1889   consoleW = CW_USEDEFAULT;\r
1890   consoleH = CW_USEDEFAULT;\r
1891   analysisX = CW_USEDEFAULT; \r
1892   analysisY = CW_USEDEFAULT; \r
1893   analysisW = CW_USEDEFAULT;\r
1894   analysisH = CW_USEDEFAULT;\r
1895   commentX = CW_USEDEFAULT; \r
1896   commentY = CW_USEDEFAULT; \r
1897   commentW = CW_USEDEFAULT;\r
1898   commentH = CW_USEDEFAULT;\r
1899   editTagsX = CW_USEDEFAULT; \r
1900   editTagsY = CW_USEDEFAULT; \r
1901   editTagsW = CW_USEDEFAULT;\r
1902   editTagsH = CW_USEDEFAULT;\r
1903   gameListX = CW_USEDEFAULT; \r
1904   gameListY = CW_USEDEFAULT; \r
1905   gameListW = CW_USEDEFAULT;\r
1906   gameListH = CW_USEDEFAULT;\r
1907   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1908   icsNames = ICS_NAMES;\r
1909   firstChessProgramNames = FCP_NAMES;\r
1910   secondChessProgramNames = SCP_NAMES;\r
1911   appData.initialMode = "";\r
1912   appData.variant = "normal";\r
1913   appData.firstProtocolVersion = PROTOVER;\r
1914   appData.secondProtocolVersion = PROTOVER;\r
1915   appData.showButtonBar = TRUE;\r
1916 \r
1917    /* [AS] New properties (see comments in header file) */\r
1918   appData.firstScoreIsAbsolute = FALSE;\r
1919   appData.secondScoreIsAbsolute = FALSE;\r
1920   appData.saveExtendedInfoInPGN = FALSE;\r
1921   appData.hideThinkingFromHuman = FALSE;\r
1922   appData.liteBackTextureFile = "";\r
1923   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1924   appData.darkBackTextureFile = "";\r
1925   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1926   appData.renderPiecesWithFont = "";\r
1927   appData.fontToPieceTable = "";\r
1928   appData.fontBackColorWhite = 0;\r
1929   appData.fontForeColorWhite = 0;\r
1930   appData.fontBackColorBlack = 0;\r
1931   appData.fontForeColorBlack = 0;\r
1932   appData.fontPieceSize = 80;\r
1933   appData.overrideLineGap = 1;\r
1934   appData.adjudicateLossThreshold = 0;\r
1935   appData.delayBeforeQuit = 0;\r
1936   appData.delayAfterQuit = 0;\r
1937   appData.nameOfDebugFile = "winboard.debug";\r
1938   appData.pgnEventHeader = "Computer Chess Game";\r
1939   appData.defaultFrcPosition = -1;\r
1940   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1941   appData.saveOutOfBookInfo = TRUE;\r
1942   appData.showEvalInMoveHistory = TRUE;\r
1943   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1944   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1945   appData.highlightMoveWithArrow = FALSE;\r
1946   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1947   appData.useStickyWindows = TRUE;\r
1948   appData.adjudicateDrawMoves = 0;\r
1949   appData.autoDisplayComment = TRUE;\r
1950   appData.autoDisplayTags = TRUE;\r
1951   appData.firstIsUCI = FALSE;\r
1952   appData.secondIsUCI = FALSE;\r
1953   appData.firstHasOwnBookUCI = TRUE;\r
1954   appData.secondHasOwnBookUCI = TRUE;\r
1955   appData.polyglotDir = "";\r
1956   appData.usePolyglotBook = FALSE;\r
1957   appData.polyglotBook = "";\r
1958   appData.defaultHashSize = 64;\r
1959   appData.defaultCacheSizeEGTB = 4;\r
1960   appData.defaultPathEGTB = "c:\\egtb";\r
1961   appData.firstOptions = "";\r
1962   appData.secondOptions = "";\r
1963 \r
1964   InitWindowPlacement( &wpMoveHistory );\r
1965   InitWindowPlacement( &wpEvalGraph );\r
1966   InitWindowPlacement( &wpEngineOutput );\r
1967 \r
1968   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1969   appData.NrFiles      = -1;\r
1970   appData.NrRanks      = -1;\r
1971   appData.holdingsSize = -1;\r
1972   appData.testClaims   = FALSE;\r
1973   appData.checkMates   = FALSE;\r
1974   appData.materialDraws= FALSE;\r
1975   appData.trivialDraws = FALSE;\r
1976   appData.ruleMoves    = 51;\r
1977   appData.drawRepeats  = 6;\r
1978   appData.matchPause   = 10000;\r
1979   appData.alphaRank    = FALSE;\r
1980   appData.allWhite     = FALSE;\r
1981   appData.upsideDown   = FALSE;\r
1982   appData.serverPause  = 15;\r
1983   appData.serverMovesName   = NULL;\r
1984   appData.suppressLoadMoves = FALSE;\r
1985   appData.firstTimeOdds  = 1;\r
1986   appData.secondTimeOdds = 1;\r
1987   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1988   appData.secondAccumulateTC = 1;\r
1989   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1990   appData.secondNPS = -1;\r
1991   appData.engineComments = 1;\r
1992   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1993   appData.egtFormats = "";\r
1994 \r
1995 #ifdef ZIPPY\r
1996   appData.zippyTalk = ZIPPY_TALK;\r
1997   appData.zippyPlay = ZIPPY_PLAY;\r
1998   appData.zippyLines = ZIPPY_LINES;\r
1999   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2000   appData.zippyPassword = ZIPPY_PASSWORD;\r
2001   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2002   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2003   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2004   appData.zippyUseI = ZIPPY_USE_I;\r
2005   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2006   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2007   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2008   appData.zippyGameStart = ZIPPY_GAME_START;\r
2009   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2010   appData.zippyAbort = ZIPPY_ABORT;\r
2011   appData.zippyVariants = ZIPPY_VARIANTS;\r
2012   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2013   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2014 #endif\r
2015 \r
2016   /* Point font array elements to structures and\r
2017      parse default font names */\r
2018   for (i=0; i<NUM_FONTS; i++) {\r
2019     for (j=0; j<NUM_SIZES; j++) {\r
2020       font[j][i] = &fontRec[j][i];\r
2021       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2022     }\r
2023   }\r
2024   \r
2025   /* Parse default settings file if any */\r
2026   if (ParseSettingsFile(settingsFileName, buf)) {\r
2027     settingsFileName = strdup(buf);\r
2028   }\r
2029 \r
2030   /* Parse command line */\r
2031   ParseArgs(StringGet, &lpCmdLine);\r
2032 \r
2033   /* [HGM] make sure board size is acceptable */\r
2034   if(appData.NrFiles > BOARD_SIZE ||\r
2035      appData.NrRanks > BOARD_SIZE   )\r
2036       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2037 \r
2038   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2039    * with options from the command line, we now make an even higher priority\r
2040    * overrule by WB options attached to the engine command line. This so that\r
2041    * tournament managers can use WB options (such as /timeOdds) that follow\r
2042    * the engines.\r
2043    */\r
2044   if(appData.firstChessProgram != NULL) {\r
2045       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2046       static char *f = "first";\r
2047       char buf[MSG_SIZ], *q = buf;\r
2048       if(p != NULL) { // engine command line contains WinBoard options\r
2049           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2050           ParseArgs(StringGet, &q);\r
2051           p[-1] = 0; // cut them offengine command line\r
2052       }\r
2053   }\r
2054   // now do same for second chess program\r
2055   if(appData.secondChessProgram != NULL) {\r
2056       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2057       static char *s = "second";\r
2058       char buf[MSG_SIZ], *q = buf;\r
2059       if(p != NULL) { // engine command line contains WinBoard options\r
2060           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2061           ParseArgs(StringGet, &q);\r
2062           p[-1] = 0; // cut them offengine command line\r
2063       }\r
2064   }\r
2065 \r
2066 \r
2067   /* Propagate options that affect others */\r
2068   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2069   if (appData.icsActive || appData.noChessProgram) {\r
2070      chessProgram = FALSE;  /* not local chess program mode */\r
2071   }\r
2072 \r
2073   /* Open startup dialog if needed */\r
2074   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2075       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2076       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2077                         *appData.secondChessProgram == NULLCHAR))) {\r
2078     FARPROC lpProc;\r
2079     \r
2080     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2081     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2082     FreeProcInstance(lpProc);\r
2083   }\r
2084 \r
2085   /* Make sure save files land in the right (?) directory */\r
2086   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2087     appData.saveGameFile = strdup(buf);\r
2088   }\r
2089   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2090     appData.savePositionFile = strdup(buf);\r
2091   }\r
2092 \r
2093   /* Finish initialization for fonts and sounds */\r
2094   for (i=0; i<NUM_FONTS; i++) {\r
2095     for (j=0; j<NUM_SIZES; j++) {\r
2096       CreateFontInMF(font[j][i]);\r
2097     }\r
2098   }\r
2099   /* xboard, and older WinBoards, controlled the move sound with the\r
2100      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2101      always turn the option on (so that the backend will call us),\r
2102      then let the user turn the sound off by setting it to silence if\r
2103      desired.  To accommodate old winboard.ini files saved by old\r
2104      versions of WinBoard, we also turn off the sound if the option\r
2105      was initially set to false. */\r
2106   if (!appData.ringBellAfterMoves) {\r
2107     sounds[(int)SoundMove].name = strdup("");\r
2108     appData.ringBellAfterMoves = TRUE;\r
2109   }\r
2110   GetCurrentDirectory(MSG_SIZ, currDir);\r
2111   SetCurrentDirectory(installDir);\r
2112   LoadAllSounds();\r
2113   SetCurrentDirectory(currDir);\r
2114 \r
2115   p = icsTextMenuString;\r
2116   if (p[0] == '@') {\r
2117     FILE* f = fopen(p + 1, "r");\r
2118     if (f == NULL) {\r
2119       DisplayFatalError(p + 1, errno, 2);\r
2120       return;\r
2121     }\r
2122     i = fread(buf, 1, sizeof(buf)-1, f);\r
2123     fclose(f);\r
2124     buf[i] = NULLCHAR;\r
2125     p = buf;\r
2126   }\r
2127   ParseIcsTextMenu(strdup(p));\r
2128 }\r
2129 \r
2130 \r
2131 VOID\r
2132 InitMenuChecks()\r
2133 {\r
2134   HMENU hmenu = GetMenu(hwndMain);\r
2135 \r
2136   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2137                         MF_BYCOMMAND|((appData.icsActive &&\r
2138                                        *appData.icsCommPort != NULLCHAR) ?\r
2139                                       MF_ENABLED : MF_GRAYED));\r
2140   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2141                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2142                                      MF_CHECKED : MF_UNCHECKED));\r
2143 }\r
2144 \r
2145 \r
2146 VOID\r
2147 SaveSettings(char* name)\r
2148 {\r
2149   FILE *f;\r
2150   ArgDescriptor *ad;\r
2151   WINDOWPLACEMENT wp;\r
2152   char dir[MSG_SIZ];\r
2153 \r
2154   if (!hwndMain) return;\r
2155 \r
2156   GetCurrentDirectory(MSG_SIZ, dir);\r
2157   SetCurrentDirectory(installDir);\r
2158   f = fopen(name, "w");\r
2159   SetCurrentDirectory(dir);\r
2160   if (f == NULL) {\r
2161     DisplayError(name, errno);\r
2162     return;\r
2163   }\r
2164   fprintf(f, ";\n");\r
2165   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2166   fprintf(f, ";\n");\r
2167   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2168   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2169   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2170   fprintf(f, ";\n");\r
2171 \r
2172   wp.length = sizeof(WINDOWPLACEMENT);\r
2173   GetWindowPlacement(hwndMain, &wp);\r
2174   boardX = wp.rcNormalPosition.left;\r
2175   boardY = wp.rcNormalPosition.top;\r
2176 \r
2177   if (hwndConsole) {\r
2178     GetWindowPlacement(hwndConsole, &wp);\r
2179     consoleX = wp.rcNormalPosition.left;\r
2180     consoleY = wp.rcNormalPosition.top;\r
2181     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2182     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2183   }\r
2184 \r
2185   if (analysisDialog) {\r
2186     GetWindowPlacement(analysisDialog, &wp);\r
2187     analysisX = wp.rcNormalPosition.left;\r
2188     analysisY = wp.rcNormalPosition.top;\r
2189     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2190     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2191   }\r
2192 \r
2193   if (commentDialog) {\r
2194     GetWindowPlacement(commentDialog, &wp);\r
2195     commentX = wp.rcNormalPosition.left;\r
2196     commentY = wp.rcNormalPosition.top;\r
2197     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2198     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2199   }\r
2200 \r
2201   if (editTagsDialog) {\r
2202     GetWindowPlacement(editTagsDialog, &wp);\r
2203     editTagsX = wp.rcNormalPosition.left;\r
2204     editTagsY = wp.rcNormalPosition.top;\r
2205     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2206     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2207   }\r
2208 \r
2209   if (gameListDialog) {\r
2210     GetWindowPlacement(gameListDialog, &wp);\r
2211     gameListX = wp.rcNormalPosition.left;\r
2212     gameListY = wp.rcNormalPosition.top;\r
2213     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2214     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2215   }\r
2216 \r
2217   /* [AS] Move history */\r
2218   wpMoveHistory.visible = MoveHistoryIsUp();\r
2219   \r
2220   if( moveHistoryDialog ) {\r
2221     GetWindowPlacement(moveHistoryDialog, &wp);\r
2222     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2223     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2224     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2225     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2226   }\r
2227 \r
2228   /* [AS] Eval graph */\r
2229   wpEvalGraph.visible = EvalGraphIsUp();\r
2230 \r
2231   if( evalGraphDialog ) {\r
2232     GetWindowPlacement(evalGraphDialog, &wp);\r
2233     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2234     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2235     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2236     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2237   }\r
2238 \r
2239   /* [AS] Engine output */\r
2240   wpEngineOutput.visible = EngineOutputIsUp();\r
2241 \r
2242   if( engineOutputDialog ) {\r
2243     GetWindowPlacement(engineOutputDialog, &wp);\r
2244     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2245     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2246     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2247     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2248   }\r
2249 \r
2250   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2251     if (!ad->save) continue;\r
2252     switch (ad->argType) {\r
2253     case ArgString:\r
2254       {\r
2255         char *p = *(char **)ad->argLoc;\r
2256         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2257           /* Quote multiline values or \-containing values\r
2258              with { } if possible */\r
2259           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2260         } else {\r
2261           /* Else quote with " " */\r
2262           fprintf(f, "/%s=\"", ad->argName);\r
2263           while (*p) {\r
2264             if (*p == '\n') fprintf(f, "\n");\r
2265             else if (*p == '\r') fprintf(f, "\\r");\r
2266             else if (*p == '\t') fprintf(f, "\\t");\r
2267             else if (*p == '\b') fprintf(f, "\\b");\r
2268             else if (*p == '\f') fprintf(f, "\\f");\r
2269             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2270             else if (*p == '\"') fprintf(f, "\\\"");\r
2271             else if (*p == '\\') fprintf(f, "\\\\");\r
2272             else putc(*p, f);\r
2273             p++;\r
2274           }\r
2275           fprintf(f, "\"\n");\r
2276         }\r
2277       }\r
2278       break;\r
2279     case ArgInt:\r
2280       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2281       break;\r
2282     case ArgFloat:\r
2283       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2284       break;\r
2285     case ArgBoolean:\r
2286       fprintf(f, "/%s=%s\n", ad->argName, \r
2287         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2288       break;\r
2289     case ArgTrue:\r
2290       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2291       break;\r
2292     case ArgFalse:\r
2293       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2294       break;\r
2295     case ArgColor:\r
2296       {\r
2297         COLORREF color = *(COLORREF *)ad->argLoc;\r
2298         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2299           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2300       }\r
2301       break;\r
2302     case ArgAttribs:\r
2303       {\r
2304         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2305         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2306           (ta->effects & CFE_BOLD) ? "b" : "",\r
2307           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2308           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2309           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2310           (ta->effects) ? " " : "",\r
2311           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2312       }\r
2313       break;\r
2314     case ArgFilename:\r
2315       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2316         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2317       } else {\r
2318         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2319       }\r
2320       break;\r
2321     case ArgBoardSize:\r
2322       fprintf(f, "/%s=%s\n", ad->argName,\r
2323               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2324       break;\r
2325     case ArgFont:\r
2326       {\r
2327         int bs;\r
2328         for (bs=0; bs<NUM_SIZES; bs++) {\r
2329           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2330           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2331           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2332             ad->argName, mfp->faceName, mfp->pointSize,\r
2333             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2334             mfp->bold ? "b" : "",\r
2335             mfp->italic ? "i" : "",\r
2336             mfp->underline ? "u" : "",\r
2337             mfp->strikeout ? "s" : "");\r
2338         }\r
2339       }\r
2340       break;\r
2341     case ArgCommSettings:\r
2342       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2343     case ArgNone:\r
2344     case ArgSettingsFilename: ;\r
2345     }\r
2346   }\r
2347   fclose(f);\r
2348 }\r
2349 \r
2350 \r
2351 \r
2352 /*---------------------------------------------------------------------------*\\r
2353  *\r
2354  * GDI board drawing routines\r
2355  *\r
2356 \*---------------------------------------------------------------------------*/\r
2357 \r
2358 /* [AS] Draw square using background texture */\r
2359 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2360 {\r
2361     XFORM   x;\r
2362 \r
2363     if( mode == 0 ) {\r
2364         return; /* Should never happen! */\r
2365     }\r
2366 \r
2367     SetGraphicsMode( dst, GM_ADVANCED );\r
2368 \r
2369     switch( mode ) {\r
2370     case 1:\r
2371         /* Identity */\r
2372         break;\r
2373     case 2:\r
2374         /* X reflection */\r
2375         x.eM11 = -1.0;\r
2376         x.eM12 = 0;\r
2377         x.eM21 = 0;\r
2378         x.eM22 = 1.0;\r
2379         x.eDx = (FLOAT) dw + dx - 1;\r
2380         x.eDy = 0;\r
2381         dx = 0;\r
2382         SetWorldTransform( dst, &x );\r
2383         break;\r
2384     case 3:\r
2385         /* Y reflection */\r
2386         x.eM11 = 1.0;\r
2387         x.eM12 = 0;\r
2388         x.eM21 = 0;\r
2389         x.eM22 = -1.0;\r
2390         x.eDx = 0;\r
2391         x.eDy = (FLOAT) dh + dy - 1;\r
2392         dy = 0;\r
2393         SetWorldTransform( dst, &x );\r
2394         break;\r
2395     case 4:\r
2396         /* X/Y flip */\r
2397         x.eM11 = 0;\r
2398         x.eM12 = 1.0;\r
2399         x.eM21 = 1.0;\r
2400         x.eM22 = 0;\r
2401         x.eDx = (FLOAT) dx;\r
2402         x.eDy = (FLOAT) dy;\r
2403         dx = 0;\r
2404         dy = 0;\r
2405         SetWorldTransform( dst, &x );\r
2406         break;\r
2407     }\r
2408 \r
2409     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2410 \r
2411     x.eM11 = 1.0;\r
2412     x.eM12 = 0;\r
2413     x.eM21 = 0;\r
2414     x.eM22 = 1.0;\r
2415     x.eDx = 0;\r
2416     x.eDy = 0;\r
2417     SetWorldTransform( dst, &x );\r
2418 \r
2419     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2420 }\r
2421 \r
2422 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2423 enum {\r
2424     PM_WP = (int) WhitePawn, \r
2425     PM_WN = (int) WhiteKnight, \r
2426     PM_WB = (int) WhiteBishop, \r
2427     PM_WR = (int) WhiteRook, \r
2428     PM_WQ = (int) WhiteQueen, \r
2429     PM_WF = (int) WhiteFerz, \r
2430     PM_WW = (int) WhiteWazir, \r
2431     PM_WE = (int) WhiteAlfil, \r
2432     PM_WM = (int) WhiteMan, \r
2433     PM_WO = (int) WhiteCannon, \r
2434     PM_WU = (int) WhiteUnicorn, \r
2435     PM_WH = (int) WhiteNightrider, \r
2436     PM_WA = (int) WhiteAngel, \r
2437     PM_WC = (int) WhiteMarshall, \r
2438     PM_WAB = (int) WhiteCardinal, \r
2439     PM_WD = (int) WhiteDragon, \r
2440     PM_WL = (int) WhiteLance, \r
2441     PM_WS = (int) WhiteCobra, \r
2442     PM_WV = (int) WhiteFalcon, \r
2443     PM_WSG = (int) WhiteSilver, \r
2444     PM_WG = (int) WhiteGrasshopper, \r
2445     PM_WK = (int) WhiteKing,\r
2446     PM_BP = (int) BlackPawn, \r
2447     PM_BN = (int) BlackKnight, \r
2448     PM_BB = (int) BlackBishop, \r
2449     PM_BR = (int) BlackRook, \r
2450     PM_BQ = (int) BlackQueen, \r
2451     PM_BF = (int) BlackFerz, \r
2452     PM_BW = (int) BlackWazir, \r
2453     PM_BE = (int) BlackAlfil, \r
2454     PM_BM = (int) BlackMan,\r
2455     PM_BO = (int) BlackCannon, \r
2456     PM_BU = (int) BlackUnicorn, \r
2457     PM_BH = (int) BlackNightrider, \r
2458     PM_BA = (int) BlackAngel, \r
2459     PM_BC = (int) BlackMarshall, \r
2460     PM_BG = (int) BlackGrasshopper, \r
2461     PM_BAB = (int) BlackCardinal,\r
2462     PM_BD = (int) BlackDragon,\r
2463     PM_BL = (int) BlackLance,\r
2464     PM_BS = (int) BlackCobra,\r
2465     PM_BV = (int) BlackFalcon,\r
2466     PM_BSG = (int) BlackSilver,\r
2467     PM_BK = (int) BlackKing\r
2468 };\r
2469 \r
2470 static HFONT hPieceFont = NULL;\r
2471 static HBITMAP hPieceMask[(int) EmptySquare];\r
2472 static HBITMAP hPieceFace[(int) EmptySquare];\r
2473 static int fontBitmapSquareSize = 0;\r
2474 static char pieceToFontChar[(int) EmptySquare] =\r
2475                               { 'p', 'n', 'b', 'r', 'q', \r
2476                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2477                       'k', 'o', 'm', 'v', 't', 'w', \r
2478                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2479                                                               'l' };\r
2480 \r
2481 extern BOOL SetCharTable( char *table, const char * map );\r
2482 /* [HGM] moved to backend.c */\r
2483 \r
2484 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2485 {\r
2486     HBRUSH hbrush;\r
2487     BYTE r1 = GetRValue( color );\r
2488     BYTE g1 = GetGValue( color );\r
2489     BYTE b1 = GetBValue( color );\r
2490     BYTE r2 = r1 / 2;\r
2491     BYTE g2 = g1 / 2;\r
2492     BYTE b2 = b1 / 2;\r
2493     RECT rc;\r
2494 \r
2495     /* Create a uniform background first */\r
2496     hbrush = CreateSolidBrush( color );\r
2497     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2498     FillRect( hdc, &rc, hbrush );\r
2499     DeleteObject( hbrush );\r
2500     \r
2501     if( mode == 1 ) {\r
2502         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2503         int steps = squareSize / 2;\r
2504         int i;\r
2505 \r
2506         for( i=0; i<steps; i++ ) {\r
2507             BYTE r = r1 - (r1-r2) * i / steps;\r
2508             BYTE g = g1 - (g1-g2) * i / steps;\r
2509             BYTE b = b1 - (b1-b2) * i / steps;\r
2510 \r
2511             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2512             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2513             FillRect( hdc, &rc, hbrush );\r
2514             DeleteObject(hbrush);\r
2515         }\r
2516     }\r
2517     else if( mode == 2 ) {\r
2518         /* Diagonal gradient, good more or less for every piece */\r
2519         POINT triangle[3];\r
2520         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2521         HBRUSH hbrush_old;\r
2522         int steps = squareSize;\r
2523         int i;\r
2524 \r
2525         triangle[0].x = squareSize - steps;\r
2526         triangle[0].y = squareSize;\r
2527         triangle[1].x = squareSize;\r
2528         triangle[1].y = squareSize;\r
2529         triangle[2].x = squareSize;\r
2530         triangle[2].y = squareSize - steps;\r
2531 \r
2532         for( i=0; i<steps; i++ ) {\r
2533             BYTE r = r1 - (r1-r2) * i / steps;\r
2534             BYTE g = g1 - (g1-g2) * i / steps;\r
2535             BYTE b = b1 - (b1-b2) * i / steps;\r
2536 \r
2537             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2538             hbrush_old = SelectObject( hdc, hbrush );\r
2539             Polygon( hdc, triangle, 3 );\r
2540             SelectObject( hdc, hbrush_old );\r
2541             DeleteObject(hbrush);\r
2542             triangle[0].x++;\r
2543             triangle[2].y++;\r
2544         }\r
2545 \r
2546         SelectObject( hdc, hpen );\r
2547     }\r
2548 }\r
2549 \r
2550 /*\r
2551     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2552     seems to work ok. The main problem here is to find the "inside" of a chess\r
2553     piece: follow the steps as explained below.\r
2554 */\r
2555 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2556 {\r
2557     HBITMAP hbm;\r
2558     HBITMAP hbm_old;\r
2559     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2560     RECT rc;\r
2561     SIZE sz;\r
2562     POINT pt;\r
2563     int backColor = whitePieceColor; \r
2564     int foreColor = blackPieceColor;\r
2565     \r
2566     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2567         backColor = appData.fontBackColorWhite;\r
2568         foreColor = appData.fontForeColorWhite;\r
2569     }\r
2570     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2571         backColor = appData.fontBackColorBlack;\r
2572         foreColor = appData.fontForeColorBlack;\r
2573     }\r
2574 \r
2575     /* Mask */\r
2576     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2577 \r
2578     hbm_old = SelectObject( hdc, hbm );\r
2579 \r
2580     rc.left = 0;\r
2581     rc.top = 0;\r
2582     rc.right = squareSize;\r
2583     rc.bottom = squareSize;\r
2584 \r
2585     /* Step 1: background is now black */\r
2586     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2587 \r
2588     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2589 \r
2590     pt.x = (squareSize - sz.cx) / 2;\r
2591     pt.y = (squareSize - sz.cy) / 2;\r
2592 \r
2593     SetBkMode( hdc, TRANSPARENT );\r
2594     SetTextColor( hdc, chroma );\r
2595     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2596     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2597 \r
2598     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2599     /* Step 3: the area outside the piece is filled with white */\r
2600 //    FloodFill( hdc, 0, 0, chroma );\r
2601     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2602     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2603     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2604     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2605     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2606     /* \r
2607         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2608         but if the start point is not inside the piece we're lost!\r
2609         There should be a better way to do this... if we could create a region or path\r
2610         from the fill operation we would be fine for example.\r
2611     */\r
2612 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2613     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2614 \r
2615     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2616         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2617         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2618 \r
2619         SelectObject( dc2, bm2 );\r
2620         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2621         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2622         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2623         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2624         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2625 \r
2626         DeleteDC( dc2 );\r
2627         DeleteObject( bm2 );\r
2628     }\r
2629 \r
2630     SetTextColor( hdc, 0 );\r
2631     /* \r
2632         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2633         draw the piece again in black for safety.\r
2634     */\r
2635     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2636 \r
2637     SelectObject( hdc, hbm_old );\r
2638 \r
2639     if( hPieceMask[index] != NULL ) {\r
2640         DeleteObject( hPieceMask[index] );\r
2641     }\r
2642 \r
2643     hPieceMask[index] = hbm;\r
2644 \r
2645     /* Face */\r
2646     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2647 \r
2648     SelectObject( hdc, hbm );\r
2649 \r
2650     {\r
2651         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2652         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2653         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2654 \r
2655         SelectObject( dc1, hPieceMask[index] );\r
2656         SelectObject( dc2, bm2 );\r
2657         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2658         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2659         \r
2660         /* \r
2661             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2662             the piece background and deletes (makes transparent) the rest.\r
2663             Thanks to that mask, we are free to paint the background with the greates\r
2664             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2665             We use this, to make gradients and give the pieces a "roundish" look.\r
2666         */\r
2667         SetPieceBackground( hdc, backColor, 2 );\r
2668         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2669 \r
2670         DeleteDC( dc2 );\r
2671         DeleteDC( dc1 );\r
2672         DeleteObject( bm2 );\r
2673     }\r
2674 \r
2675     SetTextColor( hdc, foreColor );\r
2676     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2677 \r
2678     SelectObject( hdc, hbm_old );\r
2679 \r
2680     if( hPieceFace[index] != NULL ) {\r
2681         DeleteObject( hPieceFace[index] );\r
2682     }\r
2683 \r
2684     hPieceFace[index] = hbm;\r
2685 }\r
2686 \r
2687 static int TranslatePieceToFontPiece( int piece )\r
2688 {\r
2689     switch( piece ) {\r
2690     case BlackPawn:\r
2691         return PM_BP;\r
2692     case BlackKnight:\r
2693         return PM_BN;\r
2694     case BlackBishop:\r
2695         return PM_BB;\r
2696     case BlackRook:\r
2697         return PM_BR;\r
2698     case BlackQueen:\r
2699         return PM_BQ;\r
2700     case BlackKing:\r
2701         return PM_BK;\r
2702     case WhitePawn:\r
2703         return PM_WP;\r
2704     case WhiteKnight:\r
2705         return PM_WN;\r
2706     case WhiteBishop:\r
2707         return PM_WB;\r
2708     case WhiteRook:\r
2709         return PM_WR;\r
2710     case WhiteQueen:\r
2711         return PM_WQ;\r
2712     case WhiteKing:\r
2713         return PM_WK;\r
2714 \r
2715     case BlackAngel:\r
2716         return PM_BA;\r
2717     case BlackMarshall:\r
2718         return PM_BC;\r
2719     case BlackFerz:\r
2720         return PM_BF;\r
2721     case BlackNightrider:\r
2722         return PM_BH;\r
2723     case BlackAlfil:\r
2724         return PM_BE;\r
2725     case BlackWazir:\r
2726         return PM_BW;\r
2727     case BlackUnicorn:\r
2728         return PM_BU;\r
2729     case BlackCannon:\r
2730         return PM_BO;\r
2731     case BlackGrasshopper:\r
2732         return PM_BG;\r
2733     case BlackMan:\r
2734         return PM_BM;\r
2735     case BlackSilver:\r
2736         return PM_BSG;\r
2737     case BlackLance:\r
2738         return PM_BL;\r
2739     case BlackFalcon:\r
2740         return PM_BV;\r
2741     case BlackCobra:\r
2742         return PM_BS;\r
2743     case BlackCardinal:\r
2744         return PM_BAB;\r
2745     case BlackDragon:\r
2746         return PM_BD;\r
2747 \r
2748     case WhiteAngel:\r
2749         return PM_WA;\r
2750     case WhiteMarshall:\r
2751         return PM_WC;\r
2752     case WhiteFerz:\r
2753         return PM_WF;\r
2754     case WhiteNightrider:\r
2755         return PM_WH;\r
2756     case WhiteAlfil:\r
2757         return PM_WE;\r
2758     case WhiteWazir:\r
2759         return PM_WW;\r
2760     case WhiteUnicorn:\r
2761         return PM_WU;\r
2762     case WhiteCannon:\r
2763         return PM_WO;\r
2764     case WhiteGrasshopper:\r
2765         return PM_WG;\r
2766     case WhiteMan:\r
2767         return PM_WM;\r
2768     case WhiteSilver:\r
2769         return PM_WSG;\r
2770     case WhiteLance:\r
2771         return PM_WL;\r
2772     case WhiteFalcon:\r
2773         return PM_WV;\r
2774     case WhiteCobra:\r
2775         return PM_WS;\r
2776     case WhiteCardinal:\r
2777         return PM_WAB;\r
2778     case WhiteDragon:\r
2779         return PM_WD;\r
2780     }\r
2781 \r
2782     return 0;\r
2783 }\r
2784 \r
2785 void CreatePiecesFromFont()\r
2786 {\r
2787     LOGFONT lf;\r
2788     HDC hdc_window = NULL;\r
2789     HDC hdc = NULL;\r
2790     HFONT hfont_old;\r
2791     int fontHeight;\r
2792     int i;\r
2793 \r
2794     if( fontBitmapSquareSize < 0 ) {\r
2795         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2796         return;\r
2797     }\r
2798 \r
2799     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2800         fontBitmapSquareSize = -1;\r
2801         return;\r
2802     }\r
2803 \r
2804     if( fontBitmapSquareSize != squareSize ) {\r
2805         hdc_window = GetDC( hwndMain );\r
2806         hdc = CreateCompatibleDC( hdc_window );\r
2807 \r
2808         if( hPieceFont != NULL ) {\r
2809             DeleteObject( hPieceFont );\r
2810         }\r
2811         else {\r
2812             for( i=0; i<=(int)BlackKing; i++ ) {\r
2813                 hPieceMask[i] = NULL;\r
2814                 hPieceFace[i] = NULL;\r
2815             }\r
2816         }\r
2817 \r
2818         fontHeight = 75;\r
2819 \r
2820         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2821             fontHeight = appData.fontPieceSize;\r
2822         }\r
2823 \r
2824         fontHeight = (fontHeight * squareSize) / 100;\r
2825 \r
2826         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2827         lf.lfWidth = 0;\r
2828         lf.lfEscapement = 0;\r
2829         lf.lfOrientation = 0;\r
2830         lf.lfWeight = FW_NORMAL;\r
2831         lf.lfItalic = 0;\r
2832         lf.lfUnderline = 0;\r
2833         lf.lfStrikeOut = 0;\r
2834         lf.lfCharSet = DEFAULT_CHARSET;\r
2835         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2836         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2837         lf.lfQuality = PROOF_QUALITY;\r
2838         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2839         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2840         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2841 \r
2842         hPieceFont = CreateFontIndirect( &lf );\r
2843 \r
2844         if( hPieceFont == NULL ) {\r
2845             fontBitmapSquareSize = -2;\r
2846         }\r
2847         else {\r
2848             /* Setup font-to-piece character table */\r
2849             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2850                 /* No (or wrong) global settings, try to detect the font */\r
2851                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2852                     /* Alpha */\r
2853                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2854                 }\r
2855                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2856                     /* DiagramTT* family */\r
2857                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2858                 }\r
2859                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2860                     /* Fairy symbols */\r
2861                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2862                 }\r
2863                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2864                     /* Good Companion (Some characters get warped as literal :-( */\r
2865                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2866                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2867                     SetCharTable(pieceToFontChar, s);\r
2868                 }\r
2869                 else {\r
2870                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2871                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2872                 }\r
2873             }\r
2874 \r
2875             /* Create bitmaps */\r
2876             hfont_old = SelectObject( hdc, hPieceFont );\r
2877 #if 0\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2890 \r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2923 #else\r
2924             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2925                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2926                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2927 #endif\r
2928             SelectObject( hdc, hfont_old );\r
2929 \r
2930             fontBitmapSquareSize = squareSize;\r
2931         }\r
2932     }\r
2933 \r
2934     if( hdc != NULL ) {\r
2935         DeleteDC( hdc );\r
2936     }\r
2937 \r
2938     if( hdc_window != NULL ) {\r
2939         ReleaseDC( hwndMain, hdc_window );\r
2940     }\r
2941 }\r
2942 \r
2943 HBITMAP\r
2944 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2945 {\r
2946   char name[128];\r
2947 \r
2948   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2949   if (gameInfo.event &&\r
2950       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2951       strcmp(name, "k80s") == 0) {\r
2952     strcpy(name, "tim");\r
2953   }\r
2954   return LoadBitmap(hinst, name);\r
2955 }\r
2956 \r
2957 \r
2958 /* Insert a color into the program's logical palette\r
2959    structure.  This code assumes the given color is\r
2960    the result of the RGB or PALETTERGB macro, and it\r
2961    knows how those macros work (which is documented).\r
2962 */\r
2963 VOID\r
2964 InsertInPalette(COLORREF color)\r
2965 {\r
2966   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2967 \r
2968   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2969     DisplayFatalError("Too many colors", 0, 1);\r
2970     pLogPal->palNumEntries--;\r
2971     return;\r
2972   }\r
2973 \r
2974   pe->peFlags = (char) 0;\r
2975   pe->peRed = (char) (0xFF & color);\r
2976   pe->peGreen = (char) (0xFF & (color >> 8));\r
2977   pe->peBlue = (char) (0xFF & (color >> 16));\r
2978   return;\r
2979 }\r
2980 \r
2981 \r
2982 VOID\r
2983 InitDrawingColors()\r
2984 {\r
2985   if (pLogPal == NULL) {\r
2986     /* Allocate enough memory for a logical palette with\r
2987      * PALETTESIZE entries and set the size and version fields\r
2988      * of the logical palette structure.\r
2989      */\r
2990     pLogPal = (NPLOGPALETTE)\r
2991       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2992                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2993     pLogPal->palVersion    = 0x300;\r
2994   }\r
2995   pLogPal->palNumEntries = 0;\r
2996 \r
2997   InsertInPalette(lightSquareColor);\r
2998   InsertInPalette(darkSquareColor);\r
2999   InsertInPalette(whitePieceColor);\r
3000   InsertInPalette(blackPieceColor);\r
3001   InsertInPalette(highlightSquareColor);\r
3002   InsertInPalette(premoveHighlightColor);\r
3003 \r
3004   /*  create a logical color palette according the information\r
3005    *  in the LOGPALETTE structure.\r
3006    */\r
3007   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3008 \r
3009   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3010   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3011   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3012   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3013   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3014   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3015 \r
3016   /* [AS] Force rendering of the font-based pieces */\r
3017   if( fontBitmapSquareSize > 0 ) {\r
3018     fontBitmapSquareSize = 0;\r
3019   }\r
3020 }\r
3021 \r
3022 \r
3023 int\r
3024 BoardWidth(int boardSize, int n)\r
3025 { /* [HGM] argument n added to allow different width and height */\r
3026   int lineGap = sizeInfo[boardSize].lineGap;\r
3027 \r
3028   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3029       lineGap = appData.overrideLineGap;\r
3030   }\r
3031 \r
3032   return (n + 1) * lineGap +\r
3033           n * sizeInfo[boardSize].squareSize;\r
3034 }\r
3035 \r
3036 /* Respond to board resize by dragging edge */\r
3037 VOID\r
3038 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3039 {\r
3040   BoardSize newSize = NUM_SIZES - 1;\r
3041   static int recurse = 0;\r
3042   if (IsIconic(hwndMain)) return;\r
3043   if (recurse > 0) return;\r
3044   recurse++;\r
3045   while (newSize > 0) {\r
3046         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3047         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3048            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3049     newSize--;\r
3050   } \r
3051   boardSize = newSize;\r
3052   InitDrawingSizes(boardSize, flags);\r
3053   recurse--;\r
3054 }\r
3055 \r
3056 \r
3057 \r
3058 VOID\r
3059 InitDrawingSizes(BoardSize boardSize, int flags)\r
3060 {\r
3061   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3062   ChessSquare piece;\r
3063   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3064   HDC hdc;\r
3065   SIZE clockSize, messageSize;\r
3066   HFONT oldFont;\r
3067   char buf[MSG_SIZ];\r
3068   char *str;\r
3069   HMENU hmenu = GetMenu(hwndMain);\r
3070   RECT crect, wrect;\r
3071   int offby;\r
3072   LOGBRUSH logbrush;\r
3073 \r
3074   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3075   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3076 \r
3077   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3078   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3079 \r
3080   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3081   smallLayout = sizeInfo[boardSize].smallLayout;\r
3082   squareSize = sizeInfo[boardSize].squareSize;\r
3083   lineGap = sizeInfo[boardSize].lineGap;\r
3084   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3085 \r
3086   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3087       lineGap = appData.overrideLineGap;\r
3088   }\r
3089 \r
3090   if (tinyLayout != oldTinyLayout) {\r
3091     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3092     if (tinyLayout) {\r
3093       style &= ~WS_SYSMENU;\r
3094       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3095                  "&Minimize\tCtrl+F4");\r
3096     } else {\r
3097       style |= WS_SYSMENU;\r
3098       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3099     }\r
3100     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3101 \r
3102     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3103       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3104         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3105     }\r
3106     DrawMenuBar(hwndMain);\r
3107   }\r
3108 \r
3109   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3110   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3111 \r
3112   /* Get text area sizes */\r
3113   hdc = GetDC(hwndMain);\r
3114   if (appData.clockMode) {\r
3115     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3116   } else {\r
3117     sprintf(buf, "White");\r
3118   }\r
3119   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3120   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3121   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3122   str = "We only care about the height here";\r
3123   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3124   SelectObject(hdc, oldFont);\r
3125   ReleaseDC(hwndMain, hdc);\r
3126 \r
3127   /* Compute where everything goes */\r
3128   if(first.programLogo || second.programLogo) {\r
3129         /* [HGM] logo: if either logo is on, reserve space for it */\r
3130         logoHeight =  2*clockSize.cy;\r
3131         leftLogoRect.left   = OUTER_MARGIN;\r
3132         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3133         leftLogoRect.top    = OUTER_MARGIN;\r
3134         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3135 \r
3136         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3137         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3138         rightLogoRect.top    = OUTER_MARGIN;\r
3139         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3140 \r
3141 \r
3142     blackRect.left = leftLogoRect.right;\r
3143     blackRect.right = rightLogoRect.left;\r
3144     blackRect.top = OUTER_MARGIN;\r
3145     blackRect.bottom = blackRect.top + clockSize.cy;\r
3146 \r
3147     whiteRect.left = blackRect.left ;\r
3148     whiteRect.right = blackRect.right;\r
3149     whiteRect.top = blackRect.bottom;\r
3150     whiteRect.bottom = leftLogoRect.bottom;\r
3151   } else {\r
3152     whiteRect.left = OUTER_MARGIN;\r
3153     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3154     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3155     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3156 \r
3157     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3158     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3159     blackRect.top = whiteRect.top;\r
3160     blackRect.bottom = whiteRect.bottom;\r
3161   }\r
3162 \r
3163   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3164   if (appData.showButtonBar) {\r
3165     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3166       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3167   } else {\r
3168     messageRect.right = OUTER_MARGIN + boardWidth;\r
3169   }\r
3170   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3171   messageRect.bottom = messageRect.top + messageSize.cy;\r
3172 \r
3173   boardRect.left = OUTER_MARGIN;\r
3174   boardRect.right = boardRect.left + boardWidth;\r
3175   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3176   boardRect.bottom = boardRect.top + boardHeight;\r
3177 \r
3178   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3179   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3180   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3181   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3182   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3183     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3184   GetWindowRect(hwndMain, &wrect);\r
3185   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3186                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3187   /* compensate if menu bar wrapped */\r
3188   GetClientRect(hwndMain, &crect);\r
3189   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3190   winHeight += offby;\r
3191   switch (flags) {\r
3192   case WMSZ_TOPLEFT:\r
3193     SetWindowPos(hwndMain, NULL, \r
3194                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3195                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3196     break;\r
3197 \r
3198   case WMSZ_TOPRIGHT:\r
3199   case WMSZ_TOP:\r
3200     SetWindowPos(hwndMain, NULL, \r
3201                  wrect.left, wrect.bottom - winHeight, \r
3202                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3203     break;\r
3204 \r
3205   case WMSZ_BOTTOMLEFT:\r
3206   case WMSZ_LEFT:\r
3207     SetWindowPos(hwndMain, NULL, \r
3208                  wrect.right - winWidth, wrect.top, \r
3209                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3210     break;\r
3211 \r
3212   case WMSZ_BOTTOMRIGHT:\r
3213   case WMSZ_BOTTOM:\r
3214   case WMSZ_RIGHT:\r
3215   default:\r
3216     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3217                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3218     break;\r
3219   }\r
3220 \r
3221   hwndPause = NULL;\r
3222   for (i = 0; i < N_BUTTONS; i++) {\r
3223     if (buttonDesc[i].hwnd != NULL) {\r
3224       DestroyWindow(buttonDesc[i].hwnd);\r
3225       buttonDesc[i].hwnd = NULL;\r
3226     }\r
3227     if (appData.showButtonBar) {\r
3228       buttonDesc[i].hwnd =\r
3229         CreateWindow("BUTTON", buttonDesc[i].label,\r
3230                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3231                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3232                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3233                      (HMENU) buttonDesc[i].id,\r
3234                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3235       if (tinyLayout) {\r
3236         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3237                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3238                     MAKELPARAM(FALSE, 0));\r
3239       }\r
3240       if (buttonDesc[i].id == IDM_Pause)\r
3241         hwndPause = buttonDesc[i].hwnd;\r
3242       buttonDesc[i].wndproc = (WNDPROC)\r
3243         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3244     }\r
3245   }\r
3246   if (gridPen != NULL) DeleteObject(gridPen);\r
3247   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3248   if (premovePen != NULL) DeleteObject(premovePen);\r
3249   if (lineGap != 0) {\r
3250     logbrush.lbStyle = BS_SOLID;\r
3251     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3252     gridPen =\r
3253       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3254                    lineGap, &logbrush, 0, NULL);\r
3255     logbrush.lbColor = highlightSquareColor;\r
3256     highlightPen =\r
3257       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3258                    lineGap, &logbrush, 0, NULL);\r
3259 \r
3260     logbrush.lbColor = premoveHighlightColor; \r
3261     premovePen =\r
3262       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3263                    lineGap, &logbrush, 0, NULL);\r
3264 \r
3265     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3266     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3267       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3268       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3269         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3270       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3271         BOARD_WIDTH * (squareSize + lineGap);\r
3272       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3273     }\r
3274     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3275       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3276       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3277         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3278         lineGap / 2 + (i * (squareSize + lineGap));\r
3279       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3280         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3281       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3282     }\r
3283   }\r
3284 \r
3285   /* [HGM] Licensing requirement */\r
3286 #ifdef GOTHIC\r
3287   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3288 #endif\r
3289 #ifdef FALCON\r
3290   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3291 #endif\r
3292   GothicPopUp( "", VariantNormal);\r
3293 \r
3294 \r
3295 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3296   oldBoardSize = boardSize;\r
3297   oldTinyLayout = tinyLayout;\r
3298 \r
3299   /* Load piece bitmaps for this board size */\r
3300   for (i=0; i<=2; i++) {\r
3301     for (piece = WhitePawn;\r
3302          (int) piece < (int) BlackPawn;\r
3303          piece = (ChessSquare) ((int) piece + 1)) {\r
3304       if (pieceBitmap[i][piece] != NULL)\r
3305         DeleteObject(pieceBitmap[i][piece]);\r
3306     }\r
3307   }\r
3308 \r
3309   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3310   // Orthodox Chess pieces\r
3311   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3312   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3313   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3314   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3315   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3316   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3317   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3318   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3319   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3320   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3321   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3322   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3323   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3324   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3325   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3326   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3327     // in Shogi, Hijack the unused Queen for Lance\r
3328     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3329     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3330     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3331   } else {\r
3332     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3333     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3334     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3335   }\r
3336 \r
3337   if(squareSize <= 72 && squareSize >= 33) { \r
3338     /* A & C are available in most sizes now */\r
3339     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3340       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3341       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3342       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3343       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3344       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3345       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3346       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3347       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3348       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3349       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3350       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3351       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3352     } else { // Smirf-like\r
3353       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3354       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3355       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3356     }\r
3357     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3358       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3359       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3360       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3361     } else { // WinBoard standard\r
3362       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3363       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3364       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3365     }\r
3366   }\r
3367 \r
3368 \r
3369   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3370     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3371     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3372     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3373     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3374     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3375     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3376     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3377     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3378     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3379     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3380     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3381     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3382     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3383     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3384     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3385     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3386     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3387     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3388     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3389     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3390     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3391     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3392     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3393     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3394     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3395     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3396     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3397     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3398     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3399     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3400 \r
3401     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3402       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3403       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3404       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3405       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3406       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3407       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3408       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3409       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3410       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3411       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3412       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3413       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3414     } else {\r
3415       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3416       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3417       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3418       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3419       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3420       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3421       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3422       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3423       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3424       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3425       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3426       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3427     }\r
3428 \r
3429   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3430     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3431     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3432     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3433     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3434     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3435     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3436     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3437     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3438     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3439     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3440     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3441     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3442     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3443     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3444   }\r
3445 \r
3446 \r
3447   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3448   /* special Shogi support in this size */\r
3449   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3450       for (piece = WhitePawn;\r
3451            (int) piece < (int) BlackPawn;\r
3452            piece = (ChessSquare) ((int) piece + 1)) {\r
3453         if (pieceBitmap[i][piece] != NULL)\r
3454           DeleteObject(pieceBitmap[i][piece]);\r
3455       }\r
3456     }\r
3457   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3458   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3459   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3460   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3461   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3462   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3463   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3464   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3465   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3466   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3467   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3468   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3469   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3470   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3471   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3472   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3473   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3474   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3475   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3476   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3477   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3478   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3479   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3480   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3481   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3482   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3483   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3484   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3485   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3486   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3487   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3488   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3489   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3490   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3491   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3492   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3493   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3494   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3495   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3496   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3497   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3498   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3499   minorSize = 0;\r
3500   }\r
3501 }\r
3502 \r
3503 HBITMAP\r
3504 PieceBitmap(ChessSquare p, int kind)\r
3505 {\r
3506   if ((int) p >= (int) BlackPawn)\r
3507     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3508 \r
3509   return pieceBitmap[kind][(int) p];\r
3510 }\r
3511 \r
3512 /***************************************************************/\r
3513 \r
3514 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3515 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3516 /*\r
3517 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3518 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3519 */\r
3520 \r
3521 VOID\r
3522 SquareToPos(int row, int column, int * x, int * y)\r
3523 {\r
3524   if (flipView) {\r
3525     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3526     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3527   } else {\r
3528     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3529     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3530   }\r
3531 }\r
3532 \r
3533 VOID\r
3534 DrawCoordsOnDC(HDC hdc)\r
3535 {\r
3536   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
3537   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
3538   char str[2] = { NULLCHAR, NULLCHAR };\r
3539   int oldMode, oldAlign, x, y, start, i;\r
3540   HFONT oldFont;\r
3541   HBRUSH oldBrush;\r
3542 \r
3543   if (!appData.showCoords)\r
3544     return;\r
3545 \r
3546   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3547 \r
3548   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3549   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3550   oldAlign = GetTextAlign(hdc);\r
3551   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3552 \r
3553   y = boardRect.top + lineGap;\r
3554   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3555 \r
3556   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3557   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3558     str[0] = files[start + i];\r
3559     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3560     y += squareSize + lineGap;\r
3561   }\r
3562 \r
3563   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3564 \r
3565   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3566   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3567     str[0] = ranks[start + i];\r
3568     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3569     x += squareSize + lineGap;\r
3570   }    \r
3571 \r
3572   SelectObject(hdc, oldBrush);\r
3573   SetBkMode(hdc, oldMode);\r
3574   SetTextAlign(hdc, oldAlign);\r
3575   SelectObject(hdc, oldFont);\r
3576 }\r
3577 \r
3578 VOID\r
3579 DrawGridOnDC(HDC hdc)\r
3580 {\r
3581   HPEN oldPen;\r
3582  \r
3583   if (lineGap != 0) {\r
3584     oldPen = SelectObject(hdc, gridPen);\r
3585     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3586     SelectObject(hdc, oldPen);\r
3587   }\r
3588 }\r
3589 \r
3590 #define HIGHLIGHT_PEN 0\r
3591 #define PREMOVE_PEN   1\r
3592 \r
3593 VOID\r
3594 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3595 {\r
3596   int x1, y1;\r
3597   HPEN oldPen, hPen;\r
3598   if (lineGap == 0) return;\r
3599   if (flipView) {\r
3600     x1 = boardRect.left +\r
3601       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3602     y1 = boardRect.top +\r
3603       lineGap/2 + y * (squareSize + lineGap);\r
3604   } else {\r
3605     x1 = boardRect.left +\r
3606       lineGap/2 + x * (squareSize + lineGap);\r
3607     y1 = boardRect.top +\r
3608       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3609   }\r
3610   hPen = pen ? premovePen : highlightPen;\r
3611   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3612   MoveToEx(hdc, x1, y1, NULL);\r
3613   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3614   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3615   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3616   LineTo(hdc, x1, y1);\r
3617   SelectObject(hdc, oldPen);\r
3618 }\r
3619 \r
3620 VOID\r
3621 DrawHighlightsOnDC(HDC hdc)\r
3622 {\r
3623   int i;\r
3624   for (i=0; i<2; i++) {\r
3625     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3626       DrawHighlightOnDC(hdc, TRUE,\r
3627                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3628                         HIGHLIGHT_PEN);\r
3629   }\r
3630   for (i=0; i<2; i++) {\r
3631     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3632         premoveHighlightInfo.sq[i].y >= 0) {\r
3633         DrawHighlightOnDC(hdc, TRUE,\r
3634                           premoveHighlightInfo.sq[i].x, \r
3635                           premoveHighlightInfo.sq[i].y,\r
3636                           PREMOVE_PEN);\r
3637     }\r
3638   }\r
3639 }\r
3640 \r
3641 /* Note: sqcolor is used only in monoMode */\r
3642 /* Note that this code is largely duplicated in woptions.c,\r
3643    function DrawSampleSquare, so that needs to be updated too */\r
3644 VOID\r
3645 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3646 {\r
3647   HBITMAP oldBitmap;\r
3648   HBRUSH oldBrush;\r
3649   int tmpSize;\r
3650 \r
3651   if (appData.blindfold) return;\r
3652 \r
3653   /* [AS] Use font-based pieces if needed */\r
3654   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3655     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3656     CreatePiecesFromFont();\r
3657 \r
3658     if( fontBitmapSquareSize == squareSize ) {\r
3659         int index = TranslatePieceToFontPiece(piece);\r
3660 \r
3661         SelectObject( tmphdc, hPieceMask[ index ] );\r
3662 \r
3663         BitBlt( hdc,\r
3664             x, y,\r
3665             squareSize, squareSize,\r
3666             tmphdc,\r
3667             0, 0,\r
3668             SRCAND );\r
3669 \r
3670         SelectObject( tmphdc, hPieceFace[ index ] );\r
3671 \r
3672         BitBlt( hdc,\r
3673             x, y,\r
3674             squareSize, squareSize,\r
3675             tmphdc,\r
3676             0, 0,\r
3677             SRCPAINT );\r
3678 \r
3679         return;\r
3680     }\r
3681   }\r
3682 \r
3683   if (appData.monoMode) {\r
3684     SelectObject(tmphdc, PieceBitmap(piece, \r
3685       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3686     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3687            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3688   } else {\r
3689     tmpSize = squareSize;\r
3690     if(minorSize &&\r
3691         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3692          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3693       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3694       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3695       x += (squareSize - minorSize)>>1;\r
3696       y += squareSize - minorSize - 2;\r
3697       tmpSize = minorSize;\r
3698     }\r
3699     if (color || appData.allWhite ) {\r
3700       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3701       if( color )\r
3702               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3703       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3704       if(appData.upsideDown && color==flipView)\r
3705         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3706       else\r
3707         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3708 #if 0\r
3709       /* Use black piece color for outline of white pieces */\r
3710       /* Not sure this looks really good (though xboard does it).\r
3711          Maybe better to have another selectable color, default black */\r
3712       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3713       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3714       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3715 #else\r
3716       /* Use black for outline of white pieces */\r
3717       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3718       if(appData.upsideDown && color==flipView)\r
3719         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3720       else\r
3721         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3722 #endif\r
3723     } else {\r
3724 #if 0\r
3725       /* Use white piece color for details of black pieces */\r
3726       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3727          WHITE_PIECE ones aren't always the right shape. */\r
3728       /* Not sure this looks really good (though xboard does it).\r
3729          Maybe better to have another selectable color, default medium gray? */\r
3730       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3731       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3732       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3733       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3734       SelectObject(hdc, blackPieceBrush);\r
3735       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3736 #else\r
3737       /* Use square color for details of black pieces */\r
3738       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3739       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3740       if(appData.upsideDown && !flipView)\r
3741         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3742       else\r
3743         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744 #endif\r
3745     }\r
3746     SelectObject(hdc, oldBrush);\r
3747     SelectObject(tmphdc, oldBitmap);\r
3748   }\r
3749 }\r
3750 \r
3751 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3752 int GetBackTextureMode( int algo )\r
3753 {\r
3754     int result = BACK_TEXTURE_MODE_DISABLED;\r
3755 \r
3756     switch( algo ) \r
3757     {\r
3758         case BACK_TEXTURE_MODE_PLAIN:\r
3759             result = 1; /* Always use identity map */\r
3760             break;\r
3761         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3762             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3763             break;\r
3764     }\r
3765 \r
3766     return result;\r
3767 }\r
3768 \r
3769 /* \r
3770     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3771     to handle redraws cleanly (as random numbers would always be different).\r
3772 */\r
3773 VOID RebuildTextureSquareInfo()\r
3774 {\r
3775     BITMAP bi;\r
3776     int lite_w = 0;\r
3777     int lite_h = 0;\r
3778     int dark_w = 0;\r
3779     int dark_h = 0;\r
3780     int row;\r
3781     int col;\r
3782 \r
3783     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3784 \r
3785     if( liteBackTexture != NULL ) {\r
3786         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3787             lite_w = bi.bmWidth;\r
3788             lite_h = bi.bmHeight;\r
3789         }\r
3790     }\r
3791 \r
3792     if( darkBackTexture != NULL ) {\r
3793         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3794             dark_w = bi.bmWidth;\r
3795             dark_h = bi.bmHeight;\r
3796         }\r
3797     }\r
3798 \r
3799     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3800         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3801             if( (col + row) & 1 ) {\r
3802                 /* Lite square */\r
3803                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3804                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3805                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3806                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3807                 }\r
3808             }\r
3809             else {\r
3810                 /* Dark square */\r
3811                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3812                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3813                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3814                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3815                 }\r
3816             }\r
3817         }\r
3818     }\r
3819 }\r
3820 \r
3821 /* [AS] Arrow highlighting support */\r
3822 \r
3823 static int A_WIDTH = 5; /* Width of arrow body */\r
3824 \r
3825 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3826 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3827 \r
3828 static double Sqr( double x )\r
3829 {\r
3830     return x*x;\r
3831 }\r
3832 \r
3833 static int Round( double x )\r
3834 {\r
3835     return (int) (x + 0.5);\r
3836 }\r
3837 \r
3838 /* Draw an arrow between two points using current settings */\r
3839 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3840 {\r
3841     POINT arrow[7];\r
3842     double dx, dy, j, k, x, y;\r
3843 \r
3844     if( d_x == s_x ) {\r
3845         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3846 \r
3847         arrow[0].x = s_x + A_WIDTH;\r
3848         arrow[0].y = s_y;\r
3849 \r
3850         arrow[1].x = s_x + A_WIDTH;\r
3851         arrow[1].y = d_y - h;\r
3852 \r
3853         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3854         arrow[2].y = d_y - h;\r
3855 \r
3856         arrow[3].x = d_x;\r
3857         arrow[3].y = d_y;\r
3858 \r
3859         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3860         arrow[4].y = d_y - h;\r
3861 \r
3862         arrow[5].x = s_x - A_WIDTH;\r
3863         arrow[5].y = d_y - h;\r
3864 \r
3865         arrow[6].x = s_x - A_WIDTH;\r
3866         arrow[6].y = s_y;\r
3867     }\r
3868     else if( d_y == s_y ) {\r
3869         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3870 \r
3871         arrow[0].x = s_x;\r
3872         arrow[0].y = s_y + A_WIDTH;\r
3873 \r
3874         arrow[1].x = d_x - w;\r
3875         arrow[1].y = s_y + A_WIDTH;\r
3876 \r
3877         arrow[2].x = d_x - w;\r
3878         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3879 \r
3880         arrow[3].x = d_x;\r
3881         arrow[3].y = d_y;\r
3882 \r
3883         arrow[4].x = d_x - w;\r
3884         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3885 \r
3886         arrow[5].x = d_x - w;\r
3887         arrow[5].y = s_y - A_WIDTH;\r
3888 \r
3889         arrow[6].x = s_x;\r
3890         arrow[6].y = s_y - A_WIDTH;\r
3891     }\r
3892     else {\r
3893         /* [AS] Needed a lot of paper for this! :-) */\r
3894         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3895         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3896   \r
3897         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3898 \r
3899         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3900 \r
3901         x = s_x;\r
3902         y = s_y;\r
3903 \r
3904         arrow[0].x = Round(x - j);\r
3905         arrow[0].y = Round(y + j*dx);\r
3906 \r
3907         arrow[1].x = Round(x + j);\r
3908         arrow[1].y = Round(y - j*dx);\r
3909 \r
3910         if( d_x > s_x ) {\r
3911             x = (double) d_x - k;\r
3912             y = (double) d_y - k*dy;\r
3913         }\r
3914         else {\r
3915             x = (double) d_x + k;\r
3916             y = (double) d_y + k*dy;\r
3917         }\r
3918 \r
3919         arrow[2].x = Round(x + j);\r
3920         arrow[2].y = Round(y - j*dx);\r
3921 \r
3922         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3923         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3924 \r
3925         arrow[4].x = d_x;\r
3926         arrow[4].y = d_y;\r
3927 \r
3928         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3929         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3930 \r
3931         arrow[6].x = Round(x - j);\r
3932         arrow[6].y = Round(y + j*dx);\r
3933     }\r
3934 \r
3935     Polygon( hdc, arrow, 7 );\r
3936 }\r
3937 \r
3938 /* [AS] Draw an arrow between two squares */\r
3939 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3940 {\r
3941     int s_x, s_y, d_x, d_y;\r
3942     HPEN hpen;\r
3943     HPEN holdpen;\r
3944     HBRUSH hbrush;\r
3945     HBRUSH holdbrush;\r
3946     LOGBRUSH stLB;\r
3947 \r
3948     if( s_col == d_col && s_row == d_row ) {\r
3949         return;\r
3950     }\r
3951 \r
3952     /* Get source and destination points */\r
3953     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3954     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3955 \r
3956     if( d_y > s_y ) {\r
3957         d_y += squareSize / 4;\r
3958     }\r
3959     else if( d_y < s_y ) {\r
3960         d_y += 3 * squareSize / 4;\r
3961     }\r
3962     else {\r
3963         d_y += squareSize / 2;\r
3964     }\r
3965 \r
3966     if( d_x > s_x ) {\r
3967         d_x += squareSize / 4;\r
3968     }\r
3969     else if( d_x < s_x ) {\r
3970         d_x += 3 * squareSize / 4;\r
3971     }\r
3972     else {\r
3973         d_x += squareSize / 2;\r
3974     }\r
3975 \r
3976     s_x += squareSize / 2;\r
3977     s_y += squareSize / 2;\r
3978 \r
3979     /* Adjust width */\r
3980     A_WIDTH = squareSize / 14;\r
3981 \r
3982     /* Draw */\r
3983     stLB.lbStyle = BS_SOLID;\r
3984     stLB.lbColor = appData.highlightArrowColor;\r
3985     stLB.lbHatch = 0;\r
3986 \r
3987     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3988     holdpen = SelectObject( hdc, hpen );\r
3989     hbrush = CreateBrushIndirect( &stLB );\r
3990     holdbrush = SelectObject( hdc, hbrush );\r
3991 \r
3992     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3993 \r
3994     SelectObject( hdc, holdpen );\r
3995     SelectObject( hdc, holdbrush );\r
3996     DeleteObject( hpen );\r
3997     DeleteObject( hbrush );\r
3998 }\r
3999 \r
4000 BOOL HasHighlightInfo()\r
4001 {\r
4002     BOOL result = FALSE;\r
4003 \r
4004     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4005         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4006     {\r
4007         result = TRUE;\r
4008     }\r
4009 \r
4010     return result;\r
4011 }\r
4012 \r
4013 BOOL IsDrawArrowEnabled()\r
4014 {\r
4015     BOOL result = FALSE;\r
4016 \r
4017     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4018         result = TRUE;\r
4019     }\r
4020 \r
4021     return result;\r
4022 }\r
4023 \r
4024 VOID DrawArrowHighlight( HDC hdc )\r
4025 {\r
4026     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4027         DrawArrowBetweenSquares( hdc,\r
4028             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4029             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4030     }\r
4031 }\r
4032 \r
4033 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4034 {\r
4035     HRGN result = NULL;\r
4036 \r
4037     if( HasHighlightInfo() ) {\r
4038         int x1, y1, x2, y2;\r
4039         int sx, sy, dx, dy;\r
4040 \r
4041         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4042         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4043 \r
4044         sx = MIN( x1, x2 );\r
4045         sy = MIN( y1, y2 );\r
4046         dx = MAX( x1, x2 ) + squareSize;\r
4047         dy = MAX( y1, y2 ) + squareSize;\r
4048 \r
4049         result = CreateRectRgn( sx, sy, dx, dy );\r
4050     }\r
4051 \r
4052     return result;\r
4053 }\r
4054 \r
4055 /*\r
4056     Warning: this function modifies the behavior of several other functions. \r
4057     \r
4058     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4059     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4060     repaint is scattered all over the place, which is not good for features such as\r
4061     "arrow highlighting" that require a full repaint of the board.\r
4062 \r
4063     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4064     user interaction, when speed is not so important) but especially to avoid errors\r
4065     in the displayed graphics.\r
4066 \r
4067     In such patched places, I always try refer to this function so there is a single\r
4068     place to maintain knowledge.\r
4069     \r
4070     To restore the original behavior, just return FALSE unconditionally.\r
4071 */\r
4072 BOOL IsFullRepaintPreferrable()\r
4073 {\r
4074     BOOL result = FALSE;\r
4075 \r
4076     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4077         /* Arrow may appear on the board */\r
4078         result = TRUE;\r
4079     }\r
4080 \r
4081     return result;\r
4082 }\r
4083 \r
4084 /* \r
4085     This function is called by DrawPosition to know whether a full repaint must\r
4086     be forced or not.\r
4087 \r
4088     Only DrawPosition may directly call this function, which makes use of \r
4089     some state information. Other function should call DrawPosition specifying \r
4090     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4091 */\r
4092 BOOL DrawPositionNeedsFullRepaint()\r
4093 {\r
4094     BOOL result = FALSE;\r
4095 \r
4096     /* \r
4097         Probably a slightly better policy would be to trigger a full repaint\r
4098         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4099         but animation is fast enough that it's difficult to notice.\r
4100     */\r
4101     if( animInfo.piece == EmptySquare ) {\r
4102         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4103             result = TRUE;\r
4104         }\r
4105     }\r
4106 \r
4107     return result;\r
4108 }\r
4109 \r
4110 VOID\r
4111 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4112 {\r
4113   int row, column, x, y, square_color, piece_color;\r
4114   ChessSquare piece;\r
4115   HBRUSH oldBrush;\r
4116   HDC texture_hdc = NULL;\r
4117 \r
4118   /* [AS] Initialize background textures if needed */\r
4119   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4120       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4121       if( backTextureSquareSize != squareSize \r
4122        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4123           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4124           backTextureSquareSize = squareSize;\r
4125           RebuildTextureSquareInfo();\r
4126       }\r
4127 \r
4128       texture_hdc = CreateCompatibleDC( hdc );\r
4129   }\r
4130 \r
4131   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4132     for (column = 0; column < BOARD_WIDTH; column++) {\r
4133   \r
4134       SquareToPos(row, column, &x, &y);\r
4135 \r
4136       piece = board[row][column];\r
4137 \r
4138       square_color = ((column + row) % 2) == 1;\r
4139       if( gameInfo.variant == VariantXiangqi ) {\r
4140           square_color = !InPalace(row, column);\r
4141           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4142           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4143       }\r
4144       piece_color = (int) piece < (int) BlackPawn;\r
4145 \r
4146 \r
4147       /* [HGM] holdings file: light square or black */\r
4148       if(column == BOARD_LEFT-2) {\r
4149             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4150                 square_color = 1;\r
4151             else {\r
4152                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4153                 continue;\r
4154             }\r
4155       } else\r
4156       if(column == BOARD_RGHT + 1 ) {\r
4157             if( row < gameInfo.holdingsSize )\r
4158                 square_color = 1;\r
4159             else {\r
4160                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4161                 continue;\r
4162             }\r
4163       }\r
4164       if(column == BOARD_LEFT-1 ) /* left align */\r
4165             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4166       else if( column == BOARD_RGHT) /* right align */\r
4167             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4168       else\r
4169       if (appData.monoMode) {\r
4170         if (piece == EmptySquare) {\r
4171           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4172                  square_color ? WHITENESS : BLACKNESS);\r
4173         } else {\r
4174           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4175         }\r
4176       } \r
4177       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4178           /* [AS] Draw the square using a texture bitmap */\r
4179           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4180           int r = row, c = column; // [HGM] do not flip board in flipView\r
4181           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4182 \r
4183           DrawTile( x, y, \r
4184               squareSize, squareSize, \r
4185               hdc, \r
4186               texture_hdc,\r
4187               backTextureSquareInfo[r][c].mode,\r
4188               backTextureSquareInfo[r][c].x,\r
4189               backTextureSquareInfo[r][c].y );\r
4190 \r
4191           SelectObject( texture_hdc, hbm );\r
4192 \r
4193           if (piece != EmptySquare) {\r
4194               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4195           }\r
4196       }\r
4197       else {\r
4198         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4199 \r
4200         oldBrush = SelectObject(hdc, brush );\r
4201         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4202         SelectObject(hdc, oldBrush);\r
4203         if (piece != EmptySquare)\r
4204           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4205       }\r
4206     }\r
4207   }\r
4208 \r
4209   if( texture_hdc != NULL ) {\r
4210     DeleteDC( texture_hdc );\r
4211   }\r
4212 }\r
4213 \r
4214 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4215 void fputDW(FILE *f, int x)\r
4216 {\r
4217         fputc(x     & 255, f);\r
4218         fputc(x>>8  & 255, f);\r
4219         fputc(x>>16 & 255, f);\r
4220         fputc(x>>24 & 255, f);\r
4221 }\r
4222 \r
4223 #define MAX_CLIPS 200   /* more than enough */\r
4224 \r
4225 VOID\r
4226 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4227 {\r
4228 //  HBITMAP bufferBitmap;\r
4229   BITMAP bi;\r
4230 //  RECT Rect;\r
4231   HDC tmphdc;\r
4232   HBITMAP hbm;\r
4233   int w = 100, h = 50;\r
4234 \r
4235   if(cps->programLogo == NULL) return;\r
4236 //  GetClientRect(hwndMain, &Rect);\r
4237 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4238 //                                      Rect.bottom-Rect.top+1);\r
4239   tmphdc = CreateCompatibleDC(hdc);\r
4240   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4241   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4242             w = bi.bmWidth;\r
4243             h = bi.bmHeight;\r
4244   }\r
4245   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4246                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4247   SelectObject(tmphdc, hbm);\r
4248   DeleteDC(tmphdc);\r
4249 }\r
4250 \r
4251 VOID\r
4252 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4253 {\r
4254   static Board lastReq, lastDrawn;\r
4255   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4256   static int lastDrawnFlipView = 0;\r
4257   static int lastReqValid = 0, lastDrawnValid = 0;\r
4258   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4259   HDC tmphdc;\r
4260   HDC hdcmem;\r
4261   HBITMAP bufferBitmap;\r
4262   HBITMAP oldBitmap;\r
4263   RECT Rect;\r
4264   HRGN clips[MAX_CLIPS];\r
4265   ChessSquare dragged_piece = EmptySquare;\r
4266 \r
4267   /* I'm undecided on this - this function figures out whether a full\r
4268    * repaint is necessary on its own, so there's no real reason to have the\r
4269    * caller tell it that.  I think this can safely be set to FALSE - but\r
4270    * if we trust the callers not to request full repaints unnessesarily, then\r
4271    * we could skip some clipping work.  In other words, only request a full\r
4272    * redraw when the majority of pieces have changed positions (ie. flip, \r
4273    * gamestart and similar)  --Hawk\r
4274    */\r
4275   Boolean fullrepaint = repaint;\r
4276 \r
4277   if( DrawPositionNeedsFullRepaint() ) {\r
4278       fullrepaint = TRUE;\r
4279   }\r
4280 \r
4281 #if 0\r
4282   if( fullrepaint ) {\r
4283       static int repaint_count = 0;\r
4284       char buf[128];\r
4285 \r
4286       repaint_count++;\r
4287       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4288       OutputDebugString( buf );\r
4289   }\r
4290 #endif\r
4291 \r
4292   if (board == NULL) {\r
4293     if (!lastReqValid) {\r
4294       return;\r
4295     }\r
4296     board = lastReq;\r
4297   } else {\r
4298     CopyBoard(lastReq, board);\r
4299     lastReqValid = 1;\r
4300   }\r
4301 \r
4302   if (doingSizing) {\r
4303     return;\r
4304   }\r
4305 \r
4306   if (IsIconic(hwndMain)) {\r
4307     return;\r
4308   }\r
4309 \r
4310   if (hdc == NULL) {\r
4311     hdc = GetDC(hwndMain);\r
4312     if (!appData.monoMode) {\r
4313       SelectPalette(hdc, hPal, FALSE);\r
4314       RealizePalette(hdc);\r
4315     }\r
4316     releaseDC = TRUE;\r
4317   } else {\r
4318     releaseDC = FALSE;\r
4319   }\r
4320 \r
4321 #if 0\r
4322   fprintf(debugFP, "*******************************\n"\r
4323                    "repaint = %s\n"\r
4324                    "dragInfo.from (%d,%d)\n"\r
4325                    "dragInfo.start (%d,%d)\n"\r
4326                    "dragInfo.pos (%d,%d)\n"\r
4327                    "dragInfo.lastpos (%d,%d)\n", \r
4328                     repaint ? "TRUE" : "FALSE",\r
4329                     dragInfo.from.x, dragInfo.from.y, \r
4330                     dragInfo.start.x, dragInfo.start.y,\r
4331                     dragInfo.pos.x, dragInfo.pos.y,\r
4332                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4333   fprintf(debugFP, "prev:  ");\r
4334   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4335     for (column = 0; column < BOARD_WIDTH; column++) {\r
4336       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4337     }\r
4338   }\r
4339   fprintf(debugFP, "\n");\r
4340   fprintf(debugFP, "board: ");\r
4341   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4342     for (column = 0; column < BOARD_WIDTH; column++) {\r
4343       fprintf(debugFP, "%d ", board[row][column]);\r
4344     }\r
4345   }\r
4346   fprintf(debugFP, "\n");\r
4347   fflush(debugFP);\r
4348 #endif\r
4349 \r
4350   /* Create some work-DCs */\r
4351   hdcmem = CreateCompatibleDC(hdc);\r
4352   tmphdc = CreateCompatibleDC(hdc);\r
4353 \r
4354   /* If dragging is in progress, we temporarely remove the piece */\r
4355   /* [HGM] or temporarily decrease count if stacked              */\r
4356   /*       !! Moved to before board compare !!                   */\r
4357   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4358     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4359     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4360             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4361         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4362     } else \r
4363     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4364             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4365         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4366     } else \r
4367         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4368   }\r
4369 \r
4370   /* Figure out which squares need updating by comparing the \r
4371    * newest board with the last drawn board and checking if\r
4372    * flipping has changed.\r
4373    */\r
4374   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4375     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4376       for (column = 0; column < BOARD_WIDTH; column++) {\r
4377         if (lastDrawn[row][column] != board[row][column]) {\r
4378           SquareToPos(row, column, &x, &y);\r
4379           clips[num_clips++] =\r
4380             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4381         }\r
4382       }\r
4383     }\r
4384     for (i=0; i<2; i++) {\r
4385       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4386           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4387         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4388             lastDrawnHighlight.sq[i].y >= 0) {\r
4389           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4390                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4391           clips[num_clips++] =\r
4392             CreateRectRgn(x - lineGap, y - lineGap, \r
4393                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4394         }\r
4395         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4396           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4397           clips[num_clips++] =\r
4398             CreateRectRgn(x - lineGap, y - lineGap, \r
4399                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4400         }\r
4401       }\r
4402     }\r
4403     for (i=0; i<2; i++) {\r
4404       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4405           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4406         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4407             lastDrawnPremove.sq[i].y >= 0) {\r
4408           SquareToPos(lastDrawnPremove.sq[i].y,\r
4409                       lastDrawnPremove.sq[i].x, &x, &y);\r
4410           clips[num_clips++] =\r
4411             CreateRectRgn(x - lineGap, y - lineGap, \r
4412                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4413         }\r
4414         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4415             premoveHighlightInfo.sq[i].y >= 0) {\r
4416           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4417                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4418           clips[num_clips++] =\r
4419             CreateRectRgn(x - lineGap, y - lineGap, \r
4420                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4421         }\r
4422       }\r
4423     }\r
4424   } else {\r
4425     fullrepaint = TRUE;\r
4426   }\r
4427 \r
4428   /* Create a buffer bitmap - this is the actual bitmap\r
4429    * being written to.  When all the work is done, we can\r
4430    * copy it to the real DC (the screen).  This avoids\r
4431    * the problems with flickering.\r
4432    */\r
4433   GetClientRect(hwndMain, &Rect);\r
4434   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4435                                         Rect.bottom-Rect.top+1);\r
4436   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4437   if (!appData.monoMode) {\r
4438     SelectPalette(hdcmem, hPal, FALSE);\r
4439   }\r
4440 \r
4441   /* Create clips for dragging */\r
4442   if (!fullrepaint) {\r
4443     if (dragInfo.from.x >= 0) {\r
4444       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4445       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4446     }\r
4447     if (dragInfo.start.x >= 0) {\r
4448       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4449       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4450     }\r
4451     if (dragInfo.pos.x >= 0) {\r
4452       x = dragInfo.pos.x - squareSize / 2;\r
4453       y = dragInfo.pos.y - squareSize / 2;\r
4454       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4455     }\r
4456     if (dragInfo.lastpos.x >= 0) {\r
4457       x = dragInfo.lastpos.x - squareSize / 2;\r
4458       y = dragInfo.lastpos.y - squareSize / 2;\r
4459       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4460     }\r
4461   }\r
4462 \r
4463   /* Are we animating a move?  \r
4464    * If so, \r
4465    *   - remove the piece from the board (temporarely)\r
4466    *   - calculate the clipping region\r
4467    */\r
4468   if (!fullrepaint) {\r
4469     if (animInfo.piece != EmptySquare) {\r
4470       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4471       x = boardRect.left + animInfo.lastpos.x;\r
4472       y = boardRect.top + animInfo.lastpos.y;\r
4473       x2 = boardRect.left + animInfo.pos.x;\r
4474       y2 = boardRect.top + animInfo.pos.y;\r
4475       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4476       /* Slight kludge.  The real problem is that after AnimateMove is\r
4477          done, the position on the screen does not match lastDrawn.\r
4478          This currently causes trouble only on e.p. captures in\r
4479          atomic, where the piece moves to an empty square and then\r
4480          explodes.  The old and new positions both had an empty square\r
4481          at the destination, but animation has drawn a piece there and\r
4482          we have to remember to erase it. */\r
4483       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4484     }\r
4485   }\r
4486 \r
4487   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4488   if (num_clips == 0)\r
4489     fullrepaint = TRUE;\r
4490 \r
4491   /* Set clipping on the memory DC */\r
4492   if (!fullrepaint) {\r
4493     SelectClipRgn(hdcmem, clips[0]);\r
4494     for (x = 1; x < num_clips; x++) {\r
4495       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4496         abort();  // this should never ever happen!\r
4497     }\r
4498   }\r
4499 \r
4500   /* Do all the drawing to the memory DC */\r
4501   DrawGridOnDC(hdcmem);\r
4502   DrawHighlightsOnDC(hdcmem);\r
4503   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4504 \r
4505   if(logoHeight) {\r
4506         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4507         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4508   }\r
4509 \r
4510   if( appData.highlightMoveWithArrow ) {\r
4511     DrawArrowHighlight(hdcmem);\r
4512   }\r
4513 \r
4514   DrawCoordsOnDC(hdcmem);\r
4515 \r
4516   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4517                  /* to make sure lastDrawn contains what is actually drawn */\r
4518 \r
4519   /* Put the dragged piece back into place and draw it (out of place!) */\r
4520     if (dragged_piece != EmptySquare) {\r
4521     /* [HGM] or restack */\r
4522     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4523                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4524     else\r
4525     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4526                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4527     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4528     x = dragInfo.pos.x - squareSize / 2;\r
4529     y = dragInfo.pos.y - squareSize / 2;\r
4530     DrawPieceOnDC(hdcmem, dragged_piece,\r
4531                   ((int) dragged_piece < (int) BlackPawn), \r
4532                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4533   }   \r
4534   \r
4535   /* Put the animated piece back into place and draw it */\r
4536   if (animInfo.piece != EmptySquare) {\r
4537     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4538     x = boardRect.left + animInfo.pos.x;\r
4539     y = boardRect.top + animInfo.pos.y;\r
4540     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4541                   ((int) animInfo.piece < (int) BlackPawn),\r
4542                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4543   }\r
4544 \r
4545   /* Release the bufferBitmap by selecting in the old bitmap \r
4546    * and delete the memory DC\r
4547    */\r
4548   SelectObject(hdcmem, oldBitmap);\r
4549   DeleteDC(hdcmem);\r
4550 \r
4551   /* Set clipping on the target DC */\r
4552   if (!fullrepaint) {\r
4553     SelectClipRgn(hdc, clips[0]);\r
4554     for (x = 1; x < num_clips; x++) {\r
4555       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4556         abort();   // this should never ever happen!\r
4557     } \r
4558   }\r
4559 \r
4560   /* Copy the new bitmap onto the screen in one go.\r
4561    * This way we avoid any flickering\r
4562    */\r
4563   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4564   BitBlt(hdc, boardRect.left, boardRect.top,\r
4565          boardRect.right - boardRect.left,\r
4566          boardRect.bottom - boardRect.top,\r
4567          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4568   if(saveDiagFlag) { \r
4569     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4570     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4571 \r
4572     GetObject(bufferBitmap, sizeof(b), &b);\r
4573     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4574         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4575         bih.biWidth = b.bmWidth;\r
4576         bih.biHeight = b.bmHeight;\r
4577         bih.biPlanes = 1;\r
4578         bih.biBitCount = b.bmBitsPixel;\r
4579         bih.biCompression = 0;\r
4580         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4581         bih.biXPelsPerMeter = 0;\r
4582         bih.biYPelsPerMeter = 0;\r
4583         bih.biClrUsed = 0;\r
4584         bih.biClrImportant = 0;\r
4585 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4586 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4587         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4588 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4589 \r
4590 #if 1\r
4591         wb = b.bmWidthBytes;\r
4592         // count colors\r
4593         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4594                 int k = ((int*) pData)[i];\r
4595                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4596                 if(j >= 16) break;\r
4597                 color[j] = k;\r
4598                 if(j >= nrColors) nrColors = j+1;\r
4599         }\r
4600         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4601                 INT p = 0;\r
4602                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4603                     for(w=0; w<(wb>>2); w+=2) {\r
4604                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4605                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4606                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4607                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4608                         pData[p++] = m | j<<4;\r
4609                     }\r
4610                     while(p&3) pData[p++] = 0;\r
4611                 }\r
4612                 fac = 3;\r
4613                 wb = ((wb+31)>>5)<<2;\r
4614         }\r
4615         // write BITMAPFILEHEADER\r
4616         fprintf(diagFile, "BM");\r
4617         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4618         fputDW(diagFile, 0);\r
4619         fputDW(diagFile, 0x36 + (fac?64:0));\r
4620         // write BITMAPINFOHEADER\r
4621         fputDW(diagFile, 40);\r
4622         fputDW(diagFile, b.bmWidth);\r
4623         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4624         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4625         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4626         fputDW(diagFile, 0);\r
4627         fputDW(diagFile, 0);\r
4628         fputDW(diagFile, 0);\r
4629         fputDW(diagFile, 0);\r
4630         fputDW(diagFile, 0);\r
4631         fputDW(diagFile, 0);\r
4632         // write color table\r
4633         if(fac)\r
4634         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4635         // write bitmap data\r
4636         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4637                 fputc(pData[i], diagFile);\r
4638 #endif\r
4639      }\r
4640   }\r
4641 \r
4642   SelectObject(tmphdc, oldBitmap);\r
4643 \r
4644   /* Massive cleanup */\r
4645   for (x = 0; x < num_clips; x++)\r
4646     DeleteObject(clips[x]);\r
4647 \r
4648   DeleteDC(tmphdc);\r
4649   DeleteObject(bufferBitmap);\r
4650 \r
4651   if (releaseDC) \r
4652     ReleaseDC(hwndMain, hdc);\r
4653   \r
4654   if (lastDrawnFlipView != flipView) {\r
4655     if (flipView)\r
4656       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4657     else\r
4658       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4659   }\r
4660 \r
4661 /*  CopyBoard(lastDrawn, board);*/\r
4662   lastDrawnHighlight = highlightInfo;\r
4663   lastDrawnPremove   = premoveHighlightInfo;\r
4664   lastDrawnFlipView = flipView;\r
4665   lastDrawnValid = 1;\r
4666 }\r
4667 \r
4668 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4669 int\r
4670 SaveDiagram(f)\r
4671      FILE *f;\r
4672 {\r
4673     saveDiagFlag = 1; diagFile = f;\r
4674     HDCDrawPosition(NULL, TRUE, NULL);\r
4675 \r
4676     saveDiagFlag = 0;\r
4677 \r
4678 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4679     \r
4680     fclose(f);\r
4681     return TRUE;\r
4682 }\r
4683 \r
4684 \r
4685 /*---------------------------------------------------------------------------*\\r
4686 | CLIENT PAINT PROCEDURE\r
4687 |   This is the main event-handler for the WM_PAINT message.\r
4688 |\r
4689 \*---------------------------------------------------------------------------*/\r
4690 VOID\r
4691 PaintProc(HWND hwnd)\r
4692 {\r
4693   HDC         hdc;\r
4694   PAINTSTRUCT ps;\r
4695   HFONT       oldFont;\r
4696 \r
4697   if((hdc = BeginPaint(hwnd, &ps))) {\r
4698     if (IsIconic(hwnd)) {\r
4699       DrawIcon(hdc, 2, 2, iconCurrent);\r
4700     } else {\r
4701       if (!appData.monoMode) {\r
4702         SelectPalette(hdc, hPal, FALSE);\r
4703         RealizePalette(hdc);\r
4704       }\r
4705       HDCDrawPosition(hdc, 1, NULL);\r
4706       oldFont =\r
4707         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4708       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4709                  ETO_CLIPPED|ETO_OPAQUE,\r
4710                  &messageRect, messageText, strlen(messageText), NULL);\r
4711       SelectObject(hdc, oldFont);\r
4712       DisplayBothClocks();\r
4713     }\r
4714     EndPaint(hwnd,&ps);\r
4715   }\r
4716 \r
4717   return;\r
4718 }\r
4719 \r
4720 \r
4721 /*\r
4722  * If the user selects on a border boundary, return -1; if off the board,\r
4723  *   return -2.  Otherwise map the event coordinate to the square.\r
4724  * The offset boardRect.left or boardRect.top must already have been\r
4725  *   subtracted from x.\r
4726  */\r
4727 int\r
4728 EventToSquare(int x)\r
4729 {\r
4730   if (x <= 0)\r
4731     return -2;\r
4732   if (x < lineGap)\r
4733     return -1;\r
4734   x -= lineGap;\r
4735   if ((x % (squareSize + lineGap)) >= squareSize)\r
4736     return -1;\r
4737   x /= (squareSize + lineGap);\r
4738   if (x >= BOARD_SIZE)\r
4739     return -2;\r
4740   return x;\r
4741 }\r
4742 \r
4743 typedef struct {\r
4744   char piece;\r
4745   int command;\r
4746   char* name;\r
4747 } DropEnable;\r
4748 \r
4749 DropEnable dropEnables[] = {\r
4750   { 'P', DP_Pawn, "Pawn" },\r
4751   { 'N', DP_Knight, "Knight" },\r
4752   { 'B', DP_Bishop, "Bishop" },\r
4753   { 'R', DP_Rook, "Rook" },\r
4754   { 'Q', DP_Queen, "Queen" },\r
4755 };\r
4756 \r
4757 VOID\r
4758 SetupDropMenu(HMENU hmenu)\r
4759 {\r
4760   int i, count, enable;\r
4761   char *p;\r
4762   extern char white_holding[], black_holding[];\r
4763   char item[MSG_SIZ];\r
4764 \r
4765   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4766     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4767                dropEnables[i].piece);\r
4768     count = 0;\r
4769     while (p && *p++ == dropEnables[i].piece) count++;\r
4770     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4771     enable = count > 0 || !appData.testLegality\r
4772       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4773                       && !appData.icsActive);\r
4774     ModifyMenu(hmenu, dropEnables[i].command,\r
4775                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4776                dropEnables[i].command, item);\r
4777   }\r
4778 }\r
4779 \r
4780 static int fromX = -1, fromY = -1, toX, toY;\r
4781 \r
4782 /* Event handler for mouse messages */\r
4783 VOID\r
4784 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4785 {\r
4786   int x, y;\r
4787   POINT pt;\r
4788   static int recursive = 0;\r
4789   HMENU hmenu;\r
4790 //  BOOLEAN needsRedraw = FALSE;\r
4791   BOOLEAN saveAnimate;\r
4792   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4793   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4794   ChessMove moveType;\r
4795 \r
4796   if (recursive) {\r
4797     if (message == WM_MBUTTONUP) {\r
4798       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4799          to the middle button: we simulate pressing the left button too!\r
4800          */\r
4801       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4802       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4803     }\r
4804     return;\r
4805   }\r
4806   recursive++;\r
4807   \r
4808   pt.x = LOWORD(lParam);\r
4809   pt.y = HIWORD(lParam);\r
4810   x = EventToSquare(pt.x - boardRect.left);\r
4811   y = EventToSquare(pt.y - boardRect.top);\r
4812   if (!flipView && y >= 0) {\r
4813     y = BOARD_HEIGHT - 1 - y;\r
4814   }\r
4815   if (flipView && x >= 0) {\r
4816     x = BOARD_WIDTH - 1 - x;\r
4817   }\r
4818 \r
4819   switch (message) {\r
4820   case WM_LBUTTONDOWN:\r
4821     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4822         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4823         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4824         if(gameInfo.holdingsWidth && \r
4825                 (WhiteOnMove(currentMove) \r
4826                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4827                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4828             // click in right holdings, for determining promotion piece\r
4829             ChessSquare p = boards[currentMove][y][x];\r
4830             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4831             if(p != EmptySquare) {\r
4832                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4833                 fromX = fromY = -1;\r
4834                 break;\r
4835             }\r
4836         }\r
4837         DrawPosition(FALSE, boards[currentMove]);\r
4838         break;\r
4839     }\r
4840     ErrorPopDown();\r
4841     sameAgain = FALSE;\r
4842     if (y == -2) {\r
4843       /* Downclick vertically off board; check if on clock */\r
4844       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4845         if (gameMode == EditPosition) {\r
4846           SetWhiteToPlayEvent();\r
4847         } else if (gameMode == IcsPlayingBlack ||\r
4848                    gameMode == MachinePlaysWhite) {\r
4849           CallFlagEvent();\r
4850         } else if (gameMode == EditGame) {\r
4851           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4852         }\r
4853       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4854         if (gameMode == EditPosition) {\r
4855           SetBlackToPlayEvent();\r
4856         } else if (gameMode == IcsPlayingWhite ||\r
4857                    gameMode == MachinePlaysBlack) {\r
4858           CallFlagEvent();\r
4859         } else if (gameMode == EditGame) {\r
4860           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4861         }\r
4862       }\r
4863       if (!appData.highlightLastMove) {\r
4864         ClearHighlights();\r
4865         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4866       }\r
4867       fromX = fromY = -1;\r
4868       dragInfo.start.x = dragInfo.start.y = -1;\r
4869       dragInfo.from = dragInfo.start;\r
4870       break;\r
4871     } else if (x < 0 || y < 0\r
4872       /* [HGM] block clicks between board and holdings */\r
4873               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4874               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4875               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4876         /* EditPosition, empty square, or different color piece;\r
4877            click-click move is possible */\r
4878                                ) {\r
4879       break;\r
4880     } else if (fromX == x && fromY == y) {\r
4881       /* Downclick on same square again */\r
4882       ClearHighlights();\r
4883       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4884       sameAgain = TRUE;  \r
4885     } else if (fromX != -1 &&\r
4886                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4887                                                                         ) {\r
4888       /* Downclick on different square. */\r
4889       /* [HGM] if on holdings file, should count as new first click ! */\r
4890       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4891         toX = x;\r
4892         toY = y;\r
4893         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4894            to make sure move is legal before showing promotion popup */\r
4895         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4896         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4897                 fromX = fromY = -1; \r
4898                 ClearHighlights();\r
4899                 DrawPosition(FALSE, boards[currentMove]);\r
4900                 break; \r
4901         } else \r
4902         if(moveType != ImpossibleMove) {\r
4903           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4904           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4905             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4906               appData.alwaysPromoteToQueen)) {\r
4907                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4908                   if (!appData.highlightLastMove) {\r
4909                       ClearHighlights();\r
4910                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4911                   }\r
4912           } else\r
4913           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4914                   SetHighlights(fromX, fromY, toX, toY);\r
4915                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4916                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4917                      If promotion to Q is legal, all are legal! */\r
4918                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4919                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4920                     // kludge to temporarily execute move on display, wthout promotng yet\r
4921                     promotionChoice = TRUE;\r
4922                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4923                     boards[currentMove][toY][toX] = p;\r
4924                     DrawPosition(FALSE, boards[currentMove]);\r
4925                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4926                     boards[currentMove][toY][toX] = q;\r
4927                   } else\r
4928                   PromotionPopup(hwnd);\r
4929           } else {       /* not a promotion */\r
4930              if (appData.animate || appData.highlightLastMove) {\r
4931                  SetHighlights(fromX, fromY, toX, toY);\r
4932              } else {\r
4933                  ClearHighlights();\r
4934              }\r
4935              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4936              fromX = fromY = -1;\r
4937              if (appData.animate && !appData.highlightLastMove) {\r
4938                   ClearHighlights();\r
4939                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4940              }\r
4941           }\r
4942           break;\r
4943         }\r
4944         if (gotPremove) {\r
4945             /* [HGM] it seemed that braces were missing here */\r
4946             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4947             fromX = fromY = -1;\r
4948             break;\r
4949         }\r
4950       }\r
4951       ClearHighlights();\r
4952       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4953     }\r
4954     /* First downclick, or restart on a square with same color piece */\r
4955     if (!frozen && OKToStartUserMove(x, y)) {\r
4956       fromX = x;\r
4957       fromY = y;\r
4958       dragInfo.lastpos = pt;\r
4959       dragInfo.from.x = fromX;\r
4960       dragInfo.from.y = fromY;\r
4961       dragInfo.start = dragInfo.from;\r
4962       SetCapture(hwndMain);\r
4963     } else {\r
4964       fromX = fromY = -1;\r
4965       dragInfo.start.x = dragInfo.start.y = -1;\r
4966       dragInfo.from = dragInfo.start;\r
4967       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4968     }\r
4969     break;\r
4970 \r
4971   case WM_LBUTTONUP:\r
4972     ReleaseCapture();\r
4973     if (fromX == -1) break;\r
4974     if (x == fromX && y == fromY) {\r
4975       dragInfo.from.x = dragInfo.from.y = -1;\r
4976       /* Upclick on same square */\r
4977       if (sameAgain) {\r
4978         /* Clicked same square twice: abort click-click move */\r
4979         fromX = fromY = -1;\r
4980         gotPremove = 0;\r
4981         ClearPremoveHighlights();\r
4982       } else {\r
4983         /* First square clicked: start click-click move */\r
4984         SetHighlights(fromX, fromY, -1, -1);\r
4985       }\r
4986       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4987     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4988       /* Errant click; ignore */\r
4989       break;\r
4990     } else {\r
4991       /* Finish drag move. */\r
4992     if (appData.debugMode) {\r
4993         fprintf(debugFP, "release\n");\r
4994     }\r
4995       dragInfo.from.x = dragInfo.from.y = -1;\r
4996       toX = x;\r
4997       toY = y;\r
4998       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4999       appData.animate = appData.animate && !appData.animateDragging;\r
5000       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5001       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5002                 fromX = fromY = -1; \r
5003                 ClearHighlights();\r
5004                 DrawPosition(FALSE, boards[currentMove]);\r
5005                 break; \r
5006       } else \r
5007       if(moveType != ImpossibleMove) {\r
5008           /* [HGM] use move type to determine if move is promotion.\r
5009              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5010           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5011             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5012               appData.alwaysPromoteToQueen)) \r
5013                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5014           else \r
5015           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5016                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5017                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5018                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5019                     // kludge to temporarily execute move on display, wthout promotng yet\r
5020                     promotionChoice = TRUE;\r
5021                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5022                     boards[currentMove][toY][toX] = p;\r
5023                     DrawPosition(FALSE, boards[currentMove]);\r
5024                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5025                     boards[currentMove][toY][toX] = q;\r
5026                     break;\r
5027                   } else\r
5028                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5029         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5030       }\r
5031       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5032       appData.animate = saveAnimate;\r
5033       fromX = fromY = -1;\r
5034       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5035         ClearHighlights();\r
5036       }\r
5037       if (appData.animate || appData.animateDragging ||\r
5038           appData.highlightDragging || gotPremove) {\r
5039         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5040       }\r
5041     }\r
5042     dragInfo.start.x = dragInfo.start.y = -1; \r
5043     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5044     break;\r
5045 \r
5046   case WM_MOUSEMOVE:\r
5047     if ((appData.animateDragging || appData.highlightDragging)\r
5048         && (wParam & MK_LBUTTON)\r
5049         && dragInfo.from.x >= 0) \r
5050     {\r
5051       BOOL full_repaint = FALSE;\r
5052 \r
5053       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5054       if (appData.animateDragging) {\r
5055         dragInfo.pos = pt;\r
5056       }\r
5057       if (appData.highlightDragging) {\r
5058         SetHighlights(fromX, fromY, x, y);\r
5059         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5060             full_repaint = TRUE;\r
5061         }\r
5062       }\r
5063       \r
5064       DrawPosition( full_repaint, NULL);\r
5065       \r
5066       dragInfo.lastpos = dragInfo.pos;\r
5067     }\r
5068     break;\r
5069 \r
5070   case WM_MOUSEWHEEL: // [DM]\r
5071     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5072        /* Mouse Wheel is being rolled forward\r
5073         * Play moves forward\r
5074         */\r
5075        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5076                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5077        /* Mouse Wheel is being rolled backward\r
5078         * Play moves backward\r
5079         */\r
5080        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5081                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5082     }\r
5083     break;\r
5084 \r
5085   case WM_MBUTTONDOWN:\r
5086   case WM_RBUTTONDOWN:\r
5087     ErrorPopDown();\r
5088     ReleaseCapture();\r
5089     fromX = fromY = -1;\r
5090     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5091     dragInfo.start.x = dragInfo.start.y = -1;\r
5092     dragInfo.from = dragInfo.start;\r
5093     dragInfo.lastpos = dragInfo.pos;\r
5094     if (appData.highlightDragging) {\r
5095       ClearHighlights();\r
5096     }\r
5097     if(y == -2) {\r
5098       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5099       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5100           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5101       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5102           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5103       }\r
5104     }\r
5105     DrawPosition(TRUE, NULL);\r
5106 \r
5107     switch (gameMode) {\r
5108     case EditPosition:\r
5109     case IcsExamining:\r
5110       if (x < 0 || y < 0) break;\r
5111       fromX = x;\r
5112       fromY = y;\r
5113       if (message == WM_MBUTTONDOWN) {\r
5114         buttonCount = 3;  /* even if system didn't think so */\r
5115         if (wParam & MK_SHIFT) \r
5116           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5117         else\r
5118           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5119       } else { /* message == WM_RBUTTONDOWN */\r
5120 #if 0\r
5121         if (buttonCount == 3) {\r
5122           if (wParam & MK_SHIFT) \r
5123             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5124           else\r
5125             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5126         } else {\r
5127           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5128         }\r
5129 #else\r
5130         /* Just have one menu, on the right button.  Windows users don't\r
5131            think to try the middle one, and sometimes other software steals\r
5132            it, or it doesn't really exist. */\r
5133         if(gameInfo.variant != VariantShogi)\r
5134             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5135         else\r
5136             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5137 #endif\r
5138       }\r
5139       break;\r
5140     case IcsPlayingWhite:\r
5141     case IcsPlayingBlack:\r
5142     case EditGame:\r
5143     case MachinePlaysWhite:\r
5144     case MachinePlaysBlack:\r
5145       if (appData.testLegality &&\r
5146           gameInfo.variant != VariantBughouse &&\r
5147           gameInfo.variant != VariantCrazyhouse) break;\r
5148       if (x < 0 || y < 0) break;\r
5149       fromX = x;\r
5150       fromY = y;\r
5151       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5152       SetupDropMenu(hmenu);\r
5153       MenuPopup(hwnd, pt, hmenu, -1);\r
5154       break;\r
5155     default:\r
5156       break;\r
5157     }\r
5158     break;\r
5159   }\r
5160 \r
5161   recursive--;\r
5162 }\r
5163 \r
5164 /* Preprocess messages for buttons in main window */\r
5165 LRESULT CALLBACK\r
5166 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5167 {\r
5168   int id = GetWindowLong(hwnd, GWL_ID);\r
5169   int i, dir;\r
5170 \r
5171   for (i=0; i<N_BUTTONS; i++) {\r
5172     if (buttonDesc[i].id == id) break;\r
5173   }\r
5174   if (i == N_BUTTONS) return 0;\r
5175   switch (message) {\r
5176   case WM_KEYDOWN:\r
5177     switch (wParam) {\r
5178     case VK_LEFT:\r
5179     case VK_RIGHT:\r
5180       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5181       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5182       return TRUE;\r
5183     }\r
5184     break;\r
5185   case WM_CHAR:\r
5186     switch (wParam) {\r
5187     case '\r':\r
5188       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5189       return TRUE;\r
5190     case '\t':\r
5191       if (appData.icsActive) {\r
5192         if (GetKeyState(VK_SHIFT) < 0) {\r
5193           /* shifted */\r
5194           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5195           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5196           SetFocus(h);\r
5197         } else {\r
5198           /* unshifted */\r
5199           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5200           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5201           SetFocus(h);\r
5202         }\r
5203         return TRUE;\r
5204       }\r
5205       break;\r
5206     default:\r
5207       if (appData.icsActive) {\r
5208         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5209         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5210         SetFocus(h);\r
5211         SendMessage(h, WM_CHAR, wParam, lParam);\r
5212         return TRUE;\r
5213       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5214         PopUpMoveDialog((char)wParam);\r
5215       }\r
5216       break;\r
5217     }\r
5218     break;\r
5219   }\r
5220   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5221 }\r
5222 \r
5223 /* Process messages for Promotion dialog box */\r
5224 LRESULT CALLBACK\r
5225 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5226 {\r
5227   char promoChar;\r
5228 \r
5229   switch (message) {\r
5230   case WM_INITDIALOG: /* message: initialize dialog box */\r
5231     /* Center the dialog over the application window */\r
5232     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5233     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5234       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5235        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5236                SW_SHOW : SW_HIDE);\r
5237     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5238     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5239        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5240          PieceToChar(WhiteAngel) != '~') ||\r
5241         (PieceToChar(BlackAngel) >= 'A' &&\r
5242          PieceToChar(BlackAngel) != '~')   ) ?\r
5243                SW_SHOW : SW_HIDE);\r
5244     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5245        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5246          PieceToChar(WhiteMarshall) != '~') ||\r
5247         (PieceToChar(BlackMarshall) >= 'A' &&\r
5248          PieceToChar(BlackMarshall) != '~')   ) ?\r
5249                SW_SHOW : SW_HIDE);\r
5250     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5251     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5252        gameInfo.variant != VariantShogi ?\r
5253                SW_SHOW : SW_HIDE);\r
5254     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5255        gameInfo.variant != VariantShogi ?\r
5256                SW_SHOW : SW_HIDE);\r
5257     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5258        gameInfo.variant == VariantShogi ?\r
5259                SW_SHOW : SW_HIDE);\r
5260     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5261        gameInfo.variant == VariantShogi ?\r
5262                SW_SHOW : SW_HIDE);\r
5263     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5264        gameInfo.variant == VariantSuper ?\r
5265                SW_SHOW : SW_HIDE);\r
5266     return TRUE;\r
5267 \r
5268   case WM_COMMAND: /* message: received a command */\r
5269     switch (LOWORD(wParam)) {\r
5270     case IDCANCEL:\r
5271       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5272       ClearHighlights();\r
5273       DrawPosition(FALSE, NULL);\r
5274       return TRUE;\r
5275     case PB_King:\r
5276       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5277       break;\r
5278     case PB_Queen:\r
5279       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5280       break;\r
5281     case PB_Rook:\r
5282       promoChar = PieceToChar(BlackRook);\r
5283       break;\r
5284     case PB_Bishop:\r
5285       promoChar = PieceToChar(BlackBishop);\r
5286       break;\r
5287     case PB_Chancellor:\r
5288       promoChar = PieceToChar(BlackMarshall);\r
5289       break;\r
5290     case PB_Archbishop:\r
5291       promoChar = PieceToChar(BlackAngel);\r
5292       break;\r
5293     case PB_Knight:\r
5294       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5295       break;\r
5296     default:\r
5297       return FALSE;\r
5298     }\r
5299     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5300     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5301        only show the popup when we are already sure the move is valid or\r
5302        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5303        will figure out it is a promotion from the promoChar. */\r
5304     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5305     if (!appData.highlightLastMove) {\r
5306       ClearHighlights();\r
5307       DrawPosition(FALSE, NULL);\r
5308     }\r
5309     return TRUE;\r
5310   }\r
5311   return FALSE;\r
5312 }\r
5313 \r
5314 /* Pop up promotion dialog */\r
5315 VOID\r
5316 PromotionPopup(HWND hwnd)\r
5317 {\r
5318   FARPROC lpProc;\r
5319 \r
5320   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5321   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5322     hwnd, (DLGPROC)lpProc);\r
5323   FreeProcInstance(lpProc);\r
5324 }\r
5325 \r
5326 /* Toggle ShowThinking */\r
5327 VOID\r
5328 ToggleShowThinking()\r
5329 {\r
5330   appData.showThinking = !appData.showThinking;\r
5331   ShowThinkingEvent();\r
5332 }\r
5333 \r
5334 VOID\r
5335 LoadGameDialog(HWND hwnd, char* title)\r
5336 {\r
5337   UINT number = 0;\r
5338   FILE *f;\r
5339   char fileTitle[MSG_SIZ];\r
5340   f = OpenFileDialog(hwnd, "rb", "",\r
5341                      appData.oldSaveStyle ? "gam" : "pgn",\r
5342                      GAME_FILT,\r
5343                      title, &number, fileTitle, NULL);\r
5344   if (f != NULL) {\r
5345     cmailMsgLoaded = FALSE;\r
5346     if (number == 0) {\r
5347       int error = GameListBuild(f);\r
5348       if (error) {\r
5349         DisplayError("Cannot build game list", error);\r
5350       } else if (!ListEmpty(&gameList) &&\r
5351                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5352         GameListPopUp(f, fileTitle);\r
5353         return;\r
5354       }\r
5355       GameListDestroy();\r
5356       number = 1;\r
5357     }\r
5358     LoadGame(f, number, fileTitle, FALSE);\r
5359   }\r
5360 }\r
5361 \r
5362 VOID\r
5363 ChangedConsoleFont()\r
5364 {\r
5365   CHARFORMAT cfmt;\r
5366   CHARRANGE tmpsel, sel;\r
5367   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5368   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5369   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5370   PARAFORMAT paraf;\r
5371 \r
5372   cfmt.cbSize = sizeof(CHARFORMAT);\r
5373   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5374   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5375   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5376    * size.  This was undocumented in the version of MSVC++ that I had\r
5377    * when I wrote the code, but is apparently documented now.\r
5378    */\r
5379   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5380   cfmt.bCharSet = f->lf.lfCharSet;\r
5381   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5382   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5383   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5384   /* Why are the following seemingly needed too? */\r
5385   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5386   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5387   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5388   tmpsel.cpMin = 0;\r
5389   tmpsel.cpMax = -1; /*999999?*/\r
5390   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5391   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5392   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5393    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5394    */\r
5395   paraf.cbSize = sizeof(paraf);\r
5396   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5397   paraf.dxStartIndent = 0;\r
5398   paraf.dxOffset = WRAP_INDENT;\r
5399   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5400   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5401 }\r
5402 \r
5403 /*---------------------------------------------------------------------------*\\r
5404  *\r
5405  * Window Proc for main window\r
5406  *\r
5407 \*---------------------------------------------------------------------------*/\r
5408 \r
5409 /* Process messages for main window, etc. */\r
5410 LRESULT CALLBACK\r
5411 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5412 {\r
5413   FARPROC lpProc;\r
5414   int wmId, wmEvent;\r
5415   char *defName;\r
5416   FILE *f;\r
5417   UINT number;\r
5418   char fileTitle[MSG_SIZ];\r
5419   char buf[MSG_SIZ];\r
5420   static SnapData sd;\r
5421 \r
5422   switch (message) {\r
5423 \r
5424   case WM_PAINT: /* message: repaint portion of window */\r
5425     PaintProc(hwnd);\r
5426     break;\r
5427 \r
5428   case WM_ERASEBKGND:\r
5429     if (IsIconic(hwnd)) {\r
5430       /* Cheat; change the message */\r
5431       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5432     } else {\r
5433       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5434     }\r
5435     break;\r
5436 \r
5437   case WM_LBUTTONDOWN:\r
5438   case WM_MBUTTONDOWN:\r
5439   case WM_RBUTTONDOWN:\r
5440   case WM_LBUTTONUP:\r
5441   case WM_MBUTTONUP:\r
5442   case WM_RBUTTONUP:\r
5443   case WM_MOUSEMOVE:\r
5444   case WM_MOUSEWHEEL:\r
5445     MouseEvent(hwnd, message, wParam, lParam);\r
5446     break;\r
5447 \r
5448   case WM_CHAR:\r
5449     \r
5450     if (appData.icsActive) {\r
5451       if (wParam == '\t') {\r
5452         if (GetKeyState(VK_SHIFT) < 0) {\r
5453           /* shifted */\r
5454           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5455           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5456           SetFocus(h);\r
5457         } else {\r
5458           /* unshifted */\r
5459           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5460           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5461           SetFocus(h);\r
5462         }\r
5463       } else {\r
5464         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5465         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5466         SetFocus(h);\r
5467         SendMessage(h, message, wParam, lParam);\r
5468       }\r
5469     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5470       PopUpMoveDialog((char)wParam);\r
5471     }\r
5472     break;\r
5473 \r
5474   case WM_PALETTECHANGED:\r
5475     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5476       int nnew;\r
5477       HDC hdc = GetDC(hwndMain);\r
5478       SelectPalette(hdc, hPal, TRUE);\r
5479       nnew = RealizePalette(hdc);\r
5480       if (nnew > 0) {\r
5481         paletteChanged = TRUE;\r
5482 #if 0\r
5483         UpdateColors(hdc);\r
5484 #else\r
5485         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5486 #endif\r
5487       }\r
5488       ReleaseDC(hwnd, hdc);\r
5489     }\r
5490     break;\r
5491 \r
5492   case WM_QUERYNEWPALETTE:\r
5493     if (!appData.monoMode /*&& paletteChanged*/) {\r
5494       int nnew;\r
5495       HDC hdc = GetDC(hwndMain);\r
5496       paletteChanged = FALSE;\r
5497       SelectPalette(hdc, hPal, FALSE);\r
5498       nnew = RealizePalette(hdc);\r
5499       if (nnew > 0) {\r
5500         InvalidateRect(hwnd, &boardRect, FALSE);\r
5501       }\r
5502       ReleaseDC(hwnd, hdc);\r
5503       return TRUE;\r
5504     }\r
5505     return FALSE;\r
5506 \r
5507   case WM_COMMAND: /* message: command from application menu */\r
5508     wmId    = LOWORD(wParam);\r
5509     wmEvent = HIWORD(wParam);\r
5510 \r
5511     switch (wmId) {\r
5512     case IDM_NewGame:\r
5513       ResetGameEvent();\r
5514       AnalysisPopDown();\r
5515       break;\r
5516 \r
5517     case IDM_NewGameFRC:\r
5518       if( NewGameFRC() == 0 ) {\r
5519         ResetGameEvent();\r
5520         AnalysisPopDown();\r
5521       }\r
5522       break;\r
5523 \r
5524     case IDM_NewVariant:\r
5525       NewVariantPopup(hwnd);\r
5526       break;\r
5527 \r
5528     case IDM_LoadGame:\r
5529       LoadGameDialog(hwnd, "Load Game from File");\r
5530       break;\r
5531 \r
5532     case IDM_LoadNextGame:\r
5533       ReloadGame(1);\r
5534       break;\r
5535 \r
5536     case IDM_LoadPrevGame:\r
5537       ReloadGame(-1);\r
5538       break;\r
5539 \r
5540     case IDM_ReloadGame:\r
5541       ReloadGame(0);\r
5542       break;\r
5543 \r
5544     case IDM_LoadPosition:\r
5545       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5546         Reset(FALSE, TRUE);\r
5547       }\r
5548       number = 1;\r
5549       f = OpenFileDialog(hwnd, "rb", "",\r
5550                          appData.oldSaveStyle ? "pos" : "fen",\r
5551                          POSITION_FILT,\r
5552                          "Load Position from File", &number, fileTitle, NULL);\r
5553       if (f != NULL) {\r
5554         LoadPosition(f, number, fileTitle);\r
5555       }\r
5556       break;\r
5557 \r
5558     case IDM_LoadNextPosition:\r
5559       ReloadPosition(1);\r
5560       break;\r
5561 \r
5562     case IDM_LoadPrevPosition:\r
5563       ReloadPosition(-1);\r
5564       break;\r
5565 \r
5566     case IDM_ReloadPosition:\r
5567       ReloadPosition(0);\r
5568       break;\r
5569 \r
5570     case IDM_SaveGame:\r
5571       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5572       f = OpenFileDialog(hwnd, "a", defName,\r
5573                          appData.oldSaveStyle ? "gam" : "pgn",\r
5574                          GAME_FILT,\r
5575                          "Save Game to File", NULL, fileTitle, NULL);\r
5576       if (f != NULL) {\r
5577         SaveGame(f, 0, "");\r
5578       }\r
5579       break;\r
5580 \r
5581     case IDM_SavePosition:\r
5582       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5583       f = OpenFileDialog(hwnd, "a", defName,\r
5584                          appData.oldSaveStyle ? "pos" : "fen",\r
5585                          POSITION_FILT,\r
5586                          "Save Position to File", NULL, fileTitle, NULL);\r
5587       if (f != NULL) {\r
5588         SavePosition(f, 0, "");\r
5589       }\r
5590       break;\r
5591 \r
5592     case IDM_SaveDiagram:\r
5593       defName = "diagram";\r
5594       f = OpenFileDialog(hwnd, "wb", defName,\r
5595                          "bmp",\r
5596                          DIAGRAM_FILT,\r
5597                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5598       if (f != NULL) {\r
5599         SaveDiagram(f);\r
5600       }\r
5601       break;\r
5602 \r
5603     case IDM_CopyGame:\r
5604       CopyGameToClipboard();\r
5605       break;\r
5606 \r
5607     case IDM_PasteGame:\r
5608       PasteGameFromClipboard();\r
5609       break;\r
5610 \r
5611     case IDM_CopyGameListToClipboard:\r
5612       CopyGameListToClipboard();\r
5613       break;\r
5614 \r
5615     /* [AS] Autodetect FEN or PGN data */\r
5616     case IDM_PasteAny:\r
5617       PasteGameOrFENFromClipboard();\r
5618       break;\r
5619 \r
5620     /* [AS] Move history */\r
5621     case IDM_ShowMoveHistory:\r
5622         if( MoveHistoryIsUp() ) {\r
5623             MoveHistoryPopDown();\r
5624         }\r
5625         else {\r
5626             MoveHistoryPopUp();\r
5627         }\r
5628         break;\r
5629 \r
5630     /* [AS] Eval graph */\r
5631     case IDM_ShowEvalGraph:\r
5632         if( EvalGraphIsUp() ) {\r
5633             EvalGraphPopDown();\r
5634         }\r
5635         else {\r
5636             EvalGraphPopUp();\r
5637         }\r
5638         break;\r
5639 \r
5640     /* [AS] Engine output */\r
5641     case IDM_ShowEngineOutput:\r
5642         if( EngineOutputIsUp() ) {\r
5643             EngineOutputPopDown();\r
5644         }\r
5645         else {\r
5646             EngineOutputPopUp();\r
5647         }\r
5648         break;\r
5649 \r
5650     /* [AS] User adjudication */\r
5651     case IDM_UserAdjudication_White:\r
5652         UserAdjudicationEvent( +1 );\r
5653         break;\r
5654 \r
5655     case IDM_UserAdjudication_Black:\r
5656         UserAdjudicationEvent( -1 );\r
5657         break;\r
5658 \r
5659     case IDM_UserAdjudication_Draw:\r
5660         UserAdjudicationEvent( 0 );\r
5661         break;\r
5662 \r
5663     /* [AS] Game list options dialog */\r
5664     case IDM_GameListOptions:\r
5665       GameListOptions();\r
5666       break;\r
5667 \r
5668     case IDM_CopyPosition:\r
5669       CopyFENToClipboard();\r
5670       break;\r
5671 \r
5672     case IDM_PastePosition:\r
5673       PasteFENFromClipboard();\r
5674       break;\r
5675 \r
5676     case IDM_MailMove:\r
5677       MailMoveEvent();\r
5678       break;\r
5679 \r
5680     case IDM_ReloadCMailMsg:\r
5681       Reset(TRUE, TRUE);\r
5682       ReloadCmailMsgEvent(FALSE);\r
5683       break;\r
5684 \r
5685     case IDM_Minimize:\r
5686       ShowWindow(hwnd, SW_MINIMIZE);\r
5687       break;\r
5688 \r
5689     case IDM_Exit:\r
5690       ExitEvent(0);\r
5691       break;\r
5692 \r
5693     case IDM_MachineWhite:\r
5694       MachineWhiteEvent();\r
5695       /*\r
5696        * refresh the tags dialog only if it's visible\r
5697        */\r
5698       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5699           char *tags;\r
5700           tags = PGNTags(&gameInfo);\r
5701           TagsPopUp(tags, CmailMsg());\r
5702           free(tags);\r
5703       }\r
5704       break;\r
5705 \r
5706     case IDM_MachineBlack:\r
5707       MachineBlackEvent();\r
5708       /*\r
5709        * refresh the tags dialog only if it's visible\r
5710        */\r
5711       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5712           char *tags;\r
5713           tags = PGNTags(&gameInfo);\r
5714           TagsPopUp(tags, CmailMsg());\r
5715           free(tags);\r
5716       }\r
5717       break;\r
5718 \r
5719     case IDM_TwoMachines:\r
5720       TwoMachinesEvent();\r
5721       /*\r
5722        * refresh the tags dialog only if it's visible\r
5723        */\r
5724       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5725           char *tags;\r
5726           tags = PGNTags(&gameInfo);\r
5727           TagsPopUp(tags, CmailMsg());\r
5728           free(tags);\r
5729       }\r
5730       break;\r
5731 \r
5732     case IDM_AnalysisMode:\r
5733       if (!first.analysisSupport) {\r
5734         sprintf(buf, "%s does not support analysis", first.tidy);\r
5735         DisplayError(buf, 0);\r
5736       } else {\r
5737         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5738         if (appData.icsActive) {\r
5739                if (gameMode != IcsObserving) {\r
5740                        sprintf(buf, "You are not observing a game");\r
5741                        DisplayError(buf, 0);\r
5742                        /* secure check */\r
5743                        if (appData.icsEngineAnalyze) {\r
5744                                if (appData.debugMode) \r
5745                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5746                                ExitAnalyzeMode();\r
5747                                ModeHighlight();\r
5748                                break;\r
5749                        }\r
5750                        break;\r
5751                } else {\r
5752                        /* if enable, user want disable icsEngineAnalyze */\r
5753                        if (appData.icsEngineAnalyze) {\r
5754                                ExitAnalyzeMode();\r
5755                                ModeHighlight();\r
5756                                break;\r
5757                        }\r
5758                        appData.icsEngineAnalyze = TRUE;\r
5759                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5760                }\r
5761         } \r
5762         if (!appData.showThinking) ToggleShowThinking();\r
5763         AnalyzeModeEvent();\r
5764       }\r
5765       break;\r
5766 \r
5767     case IDM_AnalyzeFile:\r
5768       if (!first.analysisSupport) {\r
5769         char buf[MSG_SIZ];\r
5770         sprintf(buf, "%s does not support analysis", first.tidy);\r
5771         DisplayError(buf, 0);\r
5772       } else {\r
5773         if (!appData.showThinking) ToggleShowThinking();\r
5774         AnalyzeFileEvent();\r
5775         LoadGameDialog(hwnd, "Analyze Game from File");\r
5776         AnalysisPeriodicEvent(1);\r
5777       }\r
5778       break;\r
5779 \r
5780     case IDM_IcsClient:\r
5781       IcsClientEvent();\r
5782       break;\r
5783 \r
5784     case IDM_EditGame:\r
5785       EditGameEvent();\r
5786       break;\r
5787 \r
5788     case IDM_EditPosition:\r
5789       EditPositionEvent();\r
5790       break;\r
5791 \r
5792     case IDM_Training:\r
5793       TrainingEvent();\r
5794       break;\r
5795 \r
5796     case IDM_ShowGameList:\r
5797       ShowGameListProc();\r
5798       break;\r
5799 \r
5800     case IDM_EditTags:\r
5801       EditTagsProc();\r
5802       break;\r
5803 \r
5804     case IDM_EditComment:\r
5805       if (commentDialogUp && editComment) {\r
5806         CommentPopDown();\r
5807       } else {\r
5808         EditCommentEvent();\r
5809       }\r
5810       break;\r
5811 \r
5812     case IDM_Pause:\r
5813       PauseEvent();\r
5814       break;\r
5815 \r
5816     case IDM_Accept:\r
5817       AcceptEvent();\r
5818       break;\r
5819 \r
5820     case IDM_Decline:\r
5821       DeclineEvent();\r
5822       break;\r
5823 \r
5824     case IDM_Rematch:\r
5825       RematchEvent();\r
5826       break;\r
5827 \r
5828     case IDM_CallFlag:\r
5829       CallFlagEvent();\r
5830       break;\r
5831 \r
5832     case IDM_Draw:\r
5833       DrawEvent();\r
5834       break;\r
5835 \r
5836     case IDM_Adjourn:\r
5837       AdjournEvent();\r
5838       break;\r
5839 \r
5840     case IDM_Abort:\r
5841       AbortEvent();\r
5842       break;\r
5843 \r
5844     case IDM_Resign:\r
5845       ResignEvent();\r
5846       break;\r
5847 \r
5848     case IDM_StopObserving:\r
5849       StopObservingEvent();\r
5850       break;\r
5851 \r
5852     case IDM_StopExamining:\r
5853       StopExaminingEvent();\r
5854       break;\r
5855 \r
5856     case IDM_TypeInMove:\r
5857       PopUpMoveDialog('\000');\r
5858       break;\r
5859 \r
5860     case IDM_TypeInName:\r
5861       PopUpNameDialog('\000');\r
5862       break;\r
5863 \r
5864     case IDM_Backward:\r
5865       BackwardEvent();\r
5866       SetFocus(hwndMain);\r
5867       break;\r
5868 \r
5869     case IDM_Forward:\r
5870       ForwardEvent();\r
5871       SetFocus(hwndMain);\r
5872       break;\r
5873 \r
5874     case IDM_ToStart:\r
5875       ToStartEvent();\r
5876       SetFocus(hwndMain);\r
5877       break;\r
5878 \r
5879     case IDM_ToEnd:\r
5880       ToEndEvent();\r
5881       SetFocus(hwndMain);\r
5882       break;\r
5883 \r
5884     case IDM_Revert:\r
5885       RevertEvent();\r
5886       break;\r
5887 \r
5888     case IDM_TruncateGame:\r
5889       TruncateGameEvent();\r
5890       break;\r
5891 \r
5892     case IDM_MoveNow:\r
5893       MoveNowEvent();\r
5894       break;\r
5895 \r
5896     case IDM_RetractMove:\r
5897       RetractMoveEvent();\r
5898       break;\r
5899 \r
5900     case IDM_FlipView:\r
5901       flipView = !flipView;\r
5902       DrawPosition(FALSE, NULL);\r
5903       break;\r
5904 \r
5905     case IDM_FlipClock:\r
5906       flipClock = !flipClock;\r
5907       DisplayBothClocks();\r
5908       break;\r
5909 \r
5910     case IDM_GeneralOptions:\r
5911       GeneralOptionsPopup(hwnd);\r
5912       DrawPosition(TRUE, NULL);\r
5913       break;\r
5914 \r
5915     case IDM_BoardOptions:\r
5916       BoardOptionsPopup(hwnd);\r
5917       break;\r
5918 \r
5919     case IDM_EnginePlayOptions:\r
5920       EnginePlayOptionsPopup(hwnd);\r
5921       break;\r
5922 \r
5923     case IDM_OptionsUCI:\r
5924       UciOptionsPopup(hwnd);\r
5925       break;\r
5926 \r
5927     case IDM_IcsOptions:\r
5928       IcsOptionsPopup(hwnd);\r
5929       break;\r
5930 \r
5931     case IDM_Fonts:\r
5932       FontsOptionsPopup(hwnd);\r
5933       break;\r
5934 \r
5935     case IDM_Sounds:\r
5936       SoundOptionsPopup(hwnd);\r
5937       break;\r
5938 \r
5939     case IDM_CommPort:\r
5940       CommPortOptionsPopup(hwnd);\r
5941       break;\r
5942 \r
5943     case IDM_LoadOptions:\r
5944       LoadOptionsPopup(hwnd);\r
5945       break;\r
5946 \r
5947     case IDM_SaveOptions:\r
5948       SaveOptionsPopup(hwnd);\r
5949       break;\r
5950 \r
5951     case IDM_TimeControl:\r
5952       TimeControlOptionsPopup(hwnd);\r
5953       break;\r
5954 \r
5955     case IDM_SaveSettings:\r
5956       SaveSettings(settingsFileName);\r
5957       break;\r
5958 \r
5959     case IDM_SaveSettingsOnExit:\r
5960       saveSettingsOnExit = !saveSettingsOnExit;\r
5961       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5962                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5963                                          MF_CHECKED : MF_UNCHECKED));\r
5964       break;\r
5965 \r
5966     case IDM_Hint:\r
5967       HintEvent();\r
5968       break;\r
5969 \r
5970     case IDM_Book:\r
5971       BookEvent();\r
5972       break;\r
5973 \r
5974     case IDM_AboutGame:\r
5975       AboutGameEvent();\r
5976       break;\r
5977 \r
5978     case IDM_Debug:\r
5979       appData.debugMode = !appData.debugMode;\r
5980       if (appData.debugMode) {\r
5981         char dir[MSG_SIZ];\r
5982         GetCurrentDirectory(MSG_SIZ, dir);\r
5983         SetCurrentDirectory(installDir);\r
5984         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5985         SetCurrentDirectory(dir);\r
5986         setbuf(debugFP, NULL);\r
5987       } else {\r
5988         fclose(debugFP);\r
5989         debugFP = NULL;\r
5990       }\r
5991       break;\r
5992 \r
5993     case IDM_HELPCONTENTS:\r
5994       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5995         MessageBox (GetFocus(),\r
5996                     "Unable to activate help",\r
5997                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5998       }\r
5999       break;\r
6000 \r
6001     case IDM_HELPSEARCH:\r
6002       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6003         MessageBox (GetFocus(),\r
6004                     "Unable to activate help",\r
6005                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6006       }\r
6007       break;\r
6008 \r
6009     case IDM_HELPHELP:\r
6010       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6011         MessageBox (GetFocus(),\r
6012                     "Unable to activate help",\r
6013                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6014       }\r
6015       break;\r
6016 \r
6017     case IDM_ABOUT:\r
6018       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6019       DialogBox(hInst, \r
6020         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6021         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6022       FreeProcInstance(lpProc);\r
6023       break;\r
6024 \r
6025     case IDM_DirectCommand1:\r
6026       AskQuestionEvent("Direct Command",\r
6027                        "Send to chess program:", "", "1");\r
6028       break;\r
6029     case IDM_DirectCommand2:\r
6030       AskQuestionEvent("Direct Command",\r
6031                        "Send to second chess program:", "", "2");\r
6032       break;\r
6033 \r
6034     case EP_WhitePawn:\r
6035       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6036       fromX = fromY = -1;\r
6037       break;\r
6038 \r
6039     case EP_WhiteKnight:\r
6040       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6041       fromX = fromY = -1;\r
6042       break;\r
6043 \r
6044     case EP_WhiteBishop:\r
6045       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6046       fromX = fromY = -1;\r
6047       break;\r
6048 \r
6049     case EP_WhiteRook:\r
6050       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6051       fromX = fromY = -1;\r
6052       break;\r
6053 \r
6054     case EP_WhiteQueen:\r
6055       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6056       fromX = fromY = -1;\r
6057       break;\r
6058 \r
6059     case EP_WhiteFerz:\r
6060       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6061       fromX = fromY = -1;\r
6062       break;\r
6063 \r
6064     case EP_WhiteWazir:\r
6065       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6066       fromX = fromY = -1;\r
6067       break;\r
6068 \r
6069     case EP_WhiteAlfil:\r
6070       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6071       fromX = fromY = -1;\r
6072       break;\r
6073 \r
6074     case EP_WhiteCannon:\r
6075       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6076       fromX = fromY = -1;\r
6077       break;\r
6078 \r
6079     case EP_WhiteCardinal:\r
6080       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6081       fromX = fromY = -1;\r
6082       break;\r
6083 \r
6084     case EP_WhiteMarshall:\r
6085       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6086       fromX = fromY = -1;\r
6087       break;\r
6088 \r
6089     case EP_WhiteKing:\r
6090       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6091       fromX = fromY = -1;\r
6092       break;\r
6093 \r
6094     case EP_BlackPawn:\r
6095       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6096       fromX = fromY = -1;\r
6097       break;\r
6098 \r
6099     case EP_BlackKnight:\r
6100       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6101       fromX = fromY = -1;\r
6102       break;\r
6103 \r
6104     case EP_BlackBishop:\r
6105       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6106       fromX = fromY = -1;\r
6107       break;\r
6108 \r
6109     case EP_BlackRook:\r
6110       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6111       fromX = fromY = -1;\r
6112       break;\r
6113 \r
6114     case EP_BlackQueen:\r
6115       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6116       fromX = fromY = -1;\r
6117       break;\r
6118 \r
6119     case EP_BlackFerz:\r
6120       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6121       fromX = fromY = -1;\r
6122       break;\r
6123 \r
6124     case EP_BlackWazir:\r
6125       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6126       fromX = fromY = -1;\r
6127       break;\r
6128 \r
6129     case EP_BlackAlfil:\r
6130       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6131       fromX = fromY = -1;\r
6132       break;\r
6133 \r
6134     case EP_BlackCannon:\r
6135       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6136       fromX = fromY = -1;\r
6137       break;\r
6138 \r
6139     case EP_BlackCardinal:\r
6140       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6141       fromX = fromY = -1;\r
6142       break;\r
6143 \r
6144     case EP_BlackMarshall:\r
6145       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6146       fromX = fromY = -1;\r
6147       break;\r
6148 \r
6149     case EP_BlackKing:\r
6150       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6151       fromX = fromY = -1;\r
6152       break;\r
6153 \r
6154     case EP_EmptySquare:\r
6155       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case EP_ClearBoard:\r
6160       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case EP_White:\r
6165       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case EP_Black:\r
6170       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case EP_Promote:\r
6175       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     case EP_Demote:\r
6180       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6181       fromX = fromY = -1;\r
6182       break;\r
6183 \r
6184     case DP_Pawn:\r
6185       DropMenuEvent(WhitePawn, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case DP_Knight:\r
6190       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case DP_Bishop:\r
6195       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case DP_Rook:\r
6200       DropMenuEvent(WhiteRook, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case DP_Queen:\r
6205       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     default:\r
6210       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6211     }\r
6212     break;\r
6213 \r
6214   case WM_TIMER:\r
6215     switch (wParam) {\r
6216     case CLOCK_TIMER_ID:\r
6217       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6218       clockTimerEvent = 0;\r
6219       DecrementClocks(); /* call into back end */\r
6220       break;\r
6221     case LOAD_GAME_TIMER_ID:\r
6222       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6223       loadGameTimerEvent = 0;\r
6224       AutoPlayGameLoop(); /* call into back end */\r
6225       break;\r
6226     case ANALYSIS_TIMER_ID:\r
6227       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6228                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6229         AnalysisPeriodicEvent(0);\r
6230       } else {\r
6231         KillTimer(hwnd, analysisTimerEvent);\r
6232         analysisTimerEvent = 0;\r
6233       }\r
6234       break;\r
6235     case DELAYED_TIMER_ID:\r
6236       KillTimer(hwnd, delayedTimerEvent);\r
6237       delayedTimerEvent = 0;\r
6238       delayedTimerCallback();\r
6239       break;\r
6240     }\r
6241     break;\r
6242 \r
6243   case WM_USER_Input:\r
6244     InputEvent(hwnd, message, wParam, lParam);\r
6245     break;\r
6246 \r
6247   /* [AS] Also move "attached" child windows */\r
6248   case WM_WINDOWPOSCHANGING:\r
6249     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6250         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6251 \r
6252         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6253             /* Window is moving */\r
6254             RECT rcMain;\r
6255 \r
6256             GetWindowRect( hwnd, &rcMain );\r
6257             \r
6258             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6259             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6260             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6261         }\r
6262     }\r
6263     break;\r
6264 \r
6265   /* [AS] Snapping */\r
6266   case WM_ENTERSIZEMOVE:\r
6267     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6268     if (hwnd == hwndMain) {\r
6269       doingSizing = TRUE;\r
6270       lastSizing = 0;\r
6271     }\r
6272     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6273     break;\r
6274 \r
6275   case WM_SIZING:\r
6276     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6277     if (hwnd == hwndMain) {\r
6278       lastSizing = wParam;\r
6279     }\r
6280     break;\r
6281 \r
6282   case WM_MOVING:\r
6283     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6284       return OnMoving( &sd, hwnd, wParam, lParam );\r
6285 \r
6286   case WM_EXITSIZEMOVE:\r
6287     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6288     if (hwnd == hwndMain) {\r
6289       RECT client;\r
6290       doingSizing = FALSE;\r
6291       InvalidateRect(hwnd, &boardRect, FALSE);\r
6292       GetClientRect(hwnd, &client);\r
6293       ResizeBoard(client.right, client.bottom, lastSizing);\r
6294       lastSizing = 0;\r
6295       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6296     }\r
6297     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6298     break;\r
6299 \r
6300   case WM_DESTROY: /* message: window being destroyed */\r
6301     PostQuitMessage(0);\r
6302     break;\r
6303 \r
6304   case WM_CLOSE:\r
6305     if (hwnd == hwndMain) {\r
6306       ExitEvent(0);\r
6307     }\r
6308     break;\r
6309 \r
6310   default:      /* Passes it on if unprocessed */\r
6311     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6312   }\r
6313   return 0;\r
6314 }\r
6315 \r
6316 /*---------------------------------------------------------------------------*\\r
6317  *\r
6318  * Misc utility routines\r
6319  *\r
6320 \*---------------------------------------------------------------------------*/\r
6321 \r
6322 /*\r
6323  * Decent random number generator, at least not as bad as Windows\r
6324  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6325  */\r
6326 unsigned int randstate;\r
6327 \r
6328 int\r
6329 myrandom(void)\r
6330 {\r
6331   randstate = randstate * 1664525 + 1013904223;\r
6332   return (int) randstate & 0x7fffffff;\r
6333 }\r
6334 \r
6335 void\r
6336 mysrandom(unsigned int seed)\r
6337 {\r
6338   randstate = seed;\r
6339 }\r
6340 \r
6341 \r
6342 /* \r
6343  * returns TRUE if user selects a different color, FALSE otherwise \r
6344  */\r
6345 \r
6346 BOOL\r
6347 ChangeColor(HWND hwnd, COLORREF *which)\r
6348 {\r
6349   static BOOL firstTime = TRUE;\r
6350   static DWORD customColors[16];\r
6351   CHOOSECOLOR cc;\r
6352   COLORREF newcolor;\r
6353   int i;\r
6354   ColorClass ccl;\r
6355 \r
6356   if (firstTime) {\r
6357     /* Make initial colors in use available as custom colors */\r
6358     /* Should we put the compiled-in defaults here instead? */\r
6359     i = 0;\r
6360     customColors[i++] = lightSquareColor & 0xffffff;\r
6361     customColors[i++] = darkSquareColor & 0xffffff;\r
6362     customColors[i++] = whitePieceColor & 0xffffff;\r
6363     customColors[i++] = blackPieceColor & 0xffffff;\r
6364     customColors[i++] = highlightSquareColor & 0xffffff;\r
6365     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6366 \r
6367     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6368       customColors[i++] = textAttribs[ccl].color;\r
6369     }\r
6370     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6371     firstTime = FALSE;\r
6372   }\r
6373 \r
6374   cc.lStructSize = sizeof(cc);\r
6375   cc.hwndOwner = hwnd;\r
6376   cc.hInstance = NULL;\r
6377   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6378   cc.lpCustColors = (LPDWORD) customColors;\r
6379   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6380 \r
6381   if (!ChooseColor(&cc)) return FALSE;\r
6382 \r
6383   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6384   if (newcolor == *which) return FALSE;\r
6385   *which = newcolor;\r
6386   return TRUE;\r
6387 \r
6388   /*\r
6389   InitDrawingColors();\r
6390   InvalidateRect(hwnd, &boardRect, FALSE);\r
6391   */\r
6392 }\r
6393 \r
6394 BOOLEAN\r
6395 MyLoadSound(MySound *ms)\r
6396 {\r
6397   BOOL ok = FALSE;\r
6398   struct stat st;\r
6399   FILE *f;\r
6400 \r
6401   if (ms->data) free(ms->data);\r
6402   ms->data = NULL;\r
6403 \r
6404   switch (ms->name[0]) {\r
6405   case NULLCHAR:\r
6406     /* Silence */\r
6407     ok = TRUE;\r
6408     break;\r
6409   case '$':\r
6410     /* System sound from Control Panel.  Don't preload here. */\r
6411     ok = TRUE;\r
6412     break;\r
6413   case '!':\r
6414     if (ms->name[1] == NULLCHAR) {\r
6415       /* "!" alone = silence */\r
6416       ok = TRUE;\r
6417     } else {\r
6418       /* Builtin wave resource.  Error if not found. */\r
6419       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6420       if (h == NULL) break;\r
6421       ms->data = (void *)LoadResource(hInst, h);\r
6422       if (h == NULL) break;\r
6423       ok = TRUE;\r
6424     }\r
6425     break;\r
6426   default:\r
6427     /* .wav file.  Error if not found. */\r
6428     f = fopen(ms->name, "rb");\r
6429     if (f == NULL) break;\r
6430     if (fstat(fileno(f), &st) < 0) break;\r
6431     ms->data = malloc(st.st_size);\r
6432     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6433     fclose(f);\r
6434     ok = TRUE;\r
6435     break;\r
6436   }\r
6437   if (!ok) {\r
6438     char buf[MSG_SIZ];\r
6439     sprintf(buf, "Error loading sound %s", ms->name);\r
6440     DisplayError(buf, GetLastError());\r
6441   }\r
6442   return ok;\r
6443 }\r
6444 \r
6445 BOOLEAN\r
6446 MyPlaySound(MySound *ms)\r
6447 {\r
6448   BOOLEAN ok = FALSE;\r
6449   switch (ms->name[0]) {\r
6450   case NULLCHAR:\r
6451     /* Silence */\r
6452     ok = TRUE;\r
6453     break;\r
6454   case '$':\r
6455     /* System sound from Control Panel (deprecated feature).\r
6456        "$" alone or an unset sound name gets default beep (still in use). */\r
6457     if (ms->name[1]) {\r
6458       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6459     }\r
6460     if (!ok) ok = MessageBeep(MB_OK);\r
6461     break; \r
6462   case '!':\r
6463     /* Builtin wave resource, or "!" alone for silence */\r
6464     if (ms->name[1]) {\r
6465       if (ms->data == NULL) return FALSE;\r
6466       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6467     } else {\r
6468       ok = TRUE;\r
6469     }\r
6470     break;\r
6471   default:\r
6472     /* .wav file.  Error if not found. */\r
6473     if (ms->data == NULL) return FALSE;\r
6474     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6475     break;\r
6476   }\r
6477   /* Don't print an error: this can happen innocently if the sound driver\r
6478      is busy; for instance, if another instance of WinBoard is playing\r
6479      a sound at about the same time. */\r
6480 #if 0\r
6481   if (!ok) {\r
6482     char buf[MSG_SIZ];\r
6483     sprintf(buf, "Error playing sound %s", ms->name);\r
6484     DisplayError(buf, GetLastError());\r
6485   }\r
6486 #endif\r
6487   return ok;\r
6488 }\r
6489 \r
6490 \r
6491 LRESULT CALLBACK\r
6492 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6493 {\r
6494   BOOL ok;\r
6495   OPENFILENAME *ofn;\r
6496   static UINT *number; /* gross that this is static */\r
6497 \r
6498   switch (message) {\r
6499   case WM_INITDIALOG: /* message: initialize dialog box */\r
6500     /* Center the dialog over the application window */\r
6501     ofn = (OPENFILENAME *) lParam;\r
6502     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6503       number = (UINT *) ofn->lCustData;\r
6504       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6505     } else {\r
6506       number = NULL;\r
6507     }\r
6508     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6509     return FALSE;  /* Allow for further processing */\r
6510 \r
6511   case WM_COMMAND:\r
6512     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6513       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6514     }\r
6515     return FALSE;  /* Allow for further processing */\r
6516   }\r
6517   return FALSE;\r
6518 }\r
6519 \r
6520 UINT APIENTRY\r
6521 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6522 {\r
6523   static UINT *number;\r
6524   OPENFILENAME *ofname;\r
6525   OFNOTIFY *ofnot;\r
6526   switch (uiMsg) {\r
6527   case WM_INITDIALOG:\r
6528     ofname = (OPENFILENAME *)lParam;\r
6529     number = (UINT *)(ofname->lCustData);\r
6530     break;\r
6531   case WM_NOTIFY:\r
6532     ofnot = (OFNOTIFY *)lParam;\r
6533     if (ofnot->hdr.code == CDN_FILEOK) {\r
6534       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6535     }\r
6536     break;\r
6537   }\r
6538   return 0;\r
6539 }\r
6540 \r
6541 \r
6542 FILE *\r
6543 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6544                char *nameFilt, char *dlgTitle, UINT *number,\r
6545                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6546 {\r
6547   OPENFILENAME openFileName;\r
6548   char buf1[MSG_SIZ];\r
6549   FILE *f;\r
6550 \r
6551   if (fileName == NULL) fileName = buf1;\r
6552   if (defName == NULL) {\r
6553     strcpy(fileName, "*.");\r
6554     strcat(fileName, defExt);\r
6555   } else {\r
6556     strcpy(fileName, defName);\r
6557   }\r
6558   if (fileTitle) strcpy(fileTitle, "");\r
6559   if (number) *number = 0;\r
6560 \r
6561   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6562   openFileName.hwndOwner         = hwnd;\r
6563   openFileName.hInstance         = (HANDLE) hInst;\r
6564   openFileName.lpstrFilter       = nameFilt;\r
6565   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6566   openFileName.nMaxCustFilter    = 0L;\r
6567   openFileName.nFilterIndex      = 1L;\r
6568   openFileName.lpstrFile         = fileName;\r
6569   openFileName.nMaxFile          = MSG_SIZ;\r
6570   openFileName.lpstrFileTitle    = fileTitle;\r
6571   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6572   openFileName.lpstrInitialDir   = NULL;\r
6573   openFileName.lpstrTitle        = dlgTitle;\r
6574   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6575     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6576     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6577     | (oldDialog ? 0 : OFN_EXPLORER);\r
6578   openFileName.nFileOffset       = 0;\r
6579   openFileName.nFileExtension    = 0;\r
6580   openFileName.lpstrDefExt       = defExt;\r
6581   openFileName.lCustData         = (LONG) number;\r
6582   openFileName.lpfnHook          = oldDialog ?\r
6583     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6584   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6585 \r
6586   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6587                         GetOpenFileName(&openFileName)) {\r
6588     /* open the file */\r
6589     f = fopen(openFileName.lpstrFile, write);\r
6590     if (f == NULL) {\r
6591       MessageBox(hwnd, "File open failed", NULL,\r
6592                  MB_OK|MB_ICONEXCLAMATION);\r
6593       return NULL;\r
6594     }\r
6595   } else {\r
6596     int err = CommDlgExtendedError();\r
6597     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6598     return FALSE;\r
6599   }\r
6600   return f;\r
6601 }\r
6602 \r
6603 \r
6604 \r
6605 VOID APIENTRY\r
6606 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6607 {\r
6608   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6609 \r
6610   /*\r
6611    * Get the first pop-up menu in the menu template. This is the\r
6612    * menu that TrackPopupMenu displays.\r
6613    */\r
6614   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6615 \r
6616   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6617 \r
6618   /*\r
6619    * TrackPopup uses screen coordinates, so convert the\r
6620    * coordinates of the mouse click to screen coordinates.\r
6621    */\r
6622   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6623 \r
6624   /* Draw and track the floating pop-up menu. */\r
6625   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6626                  pt.x, pt.y, 0, hwnd, NULL);\r
6627 \r
6628   /* Destroy the menu.*/\r
6629   DestroyMenu(hmenu);\r
6630 }\r
6631    \r
6632 typedef struct {\r
6633   HWND hDlg, hText;\r
6634   int sizeX, sizeY, newSizeX, newSizeY;\r
6635   HDWP hdwp;\r
6636 } ResizeEditPlusButtonsClosure;\r
6637 \r
6638 BOOL CALLBACK\r
6639 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6640 {\r
6641   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6642   RECT rect;\r
6643   POINT pt;\r
6644 \r
6645   if (hChild == cl->hText) return TRUE;\r
6646   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6647   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6648   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6649   ScreenToClient(cl->hDlg, &pt);\r
6650   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6651     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6652   return TRUE;\r
6653 }\r
6654 \r
6655 /* Resize a dialog that has a (rich) edit field filling most of\r
6656    the top, with a row of buttons below */\r
6657 VOID\r
6658 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6659 {\r
6660   RECT rectText;\r
6661   int newTextHeight, newTextWidth;\r
6662   ResizeEditPlusButtonsClosure cl;\r
6663   \r
6664   /*if (IsIconic(hDlg)) return;*/\r
6665   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6666   \r
6667   cl.hdwp = BeginDeferWindowPos(8);\r
6668 \r
6669   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6670   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6671   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6672   if (newTextHeight < 0) {\r
6673     newSizeY += -newTextHeight;\r
6674     newTextHeight = 0;\r
6675   }\r
6676   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6677     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6678 \r
6679   cl.hDlg = hDlg;\r
6680   cl.hText = hText;\r
6681   cl.sizeX = sizeX;\r
6682   cl.sizeY = sizeY;\r
6683   cl.newSizeX = newSizeX;\r
6684   cl.newSizeY = newSizeY;\r
6685   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6686 \r
6687   EndDeferWindowPos(cl.hdwp);\r
6688 }\r
6689 \r
6690 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6691 {\r
6692     RECT    rChild, rParent;\r
6693     int     wChild, hChild, wParent, hParent;\r
6694     int     wScreen, hScreen, xNew, yNew;\r
6695     HDC     hdc;\r
6696 \r
6697     /* Get the Height and Width of the child window */\r
6698     GetWindowRect (hwndChild, &rChild);\r
6699     wChild = rChild.right - rChild.left;\r
6700     hChild = rChild.bottom - rChild.top;\r
6701 \r
6702     /* Get the Height and Width of the parent window */\r
6703     GetWindowRect (hwndParent, &rParent);\r
6704     wParent = rParent.right - rParent.left;\r
6705     hParent = rParent.bottom - rParent.top;\r
6706 \r
6707     /* Get the display limits */\r
6708     hdc = GetDC (hwndChild);\r
6709     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6710     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6711     ReleaseDC(hwndChild, hdc);\r
6712 \r
6713     /* Calculate new X position, then adjust for screen */\r
6714     xNew = rParent.left + ((wParent - wChild) /2);\r
6715     if (xNew < 0) {\r
6716         xNew = 0;\r
6717     } else if ((xNew+wChild) > wScreen) {\r
6718         xNew = wScreen - wChild;\r
6719     }\r
6720 \r
6721     /* Calculate new Y position, then adjust for screen */\r
6722     if( mode == 0 ) {\r
6723         yNew = rParent.top  + ((hParent - hChild) /2);\r
6724     }\r
6725     else {\r
6726         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6727     }\r
6728 \r
6729     if (yNew < 0) {\r
6730         yNew = 0;\r
6731     } else if ((yNew+hChild) > hScreen) {\r
6732         yNew = hScreen - hChild;\r
6733     }\r
6734 \r
6735     /* Set it, and return */\r
6736     return SetWindowPos (hwndChild, NULL,\r
6737                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6738 }\r
6739 \r
6740 /* Center one window over another */\r
6741 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6742 {\r
6743     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6744 }\r
6745 \r
6746 /*---------------------------------------------------------------------------*\\r
6747  *\r
6748  * Startup Dialog functions\r
6749  *\r
6750 \*---------------------------------------------------------------------------*/\r
6751 void\r
6752 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6753 {\r
6754   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6755 \r
6756   while (*cd != NULL) {\r
6757     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6758     cd++;\r
6759   }\r
6760 }\r
6761 \r
6762 void\r
6763 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6764 {\r
6765   char buf1[ARG_MAX];\r
6766   int len;\r
6767 \r
6768   if (str[0] == '@') {\r
6769     FILE* f = fopen(str + 1, "r");\r
6770     if (f == NULL) {\r
6771       DisplayFatalError(str + 1, errno, 2);\r
6772       return;\r
6773     }\r
6774     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6775     fclose(f);\r
6776     buf1[len] = NULLCHAR;\r
6777     str = buf1;\r
6778   }\r
6779 \r
6780   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6781 \r
6782   for (;;) {\r
6783     char buf[MSG_SIZ];\r
6784     char *end = strchr(str, '\n');\r
6785     if (end == NULL) return;\r
6786     memcpy(buf, str, end - str);\r
6787     buf[end - str] = NULLCHAR;\r
6788     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6789     str = end + 1;\r
6790   }\r
6791 }\r
6792 \r
6793 void\r
6794 SetStartupDialogEnables(HWND hDlg)\r
6795 {\r
6796   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6797     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6798     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6799   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6800     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6801   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6802     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6803   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6804     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6805   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6806     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6807     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6808     IsDlgButtonChecked(hDlg, OPT_View));\r
6809 }\r
6810 \r
6811 char *\r
6812 QuoteForFilename(char *filename)\r
6813 {\r
6814   int dquote, space;\r
6815   dquote = strchr(filename, '"') != NULL;\r
6816   space = strchr(filename, ' ') != NULL;\r
6817   if (dquote || space) {\r
6818     if (dquote) {\r
6819       return "'";\r
6820     } else {\r
6821       return "\"";\r
6822     }\r
6823   } else {\r
6824     return "";\r
6825   }\r
6826 }\r
6827 \r
6828 VOID\r
6829 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6830 {\r
6831   char buf[MSG_SIZ];\r
6832   char *q;\r
6833 \r
6834   InitComboStringsFromOption(hwndCombo, nthnames);\r
6835   q = QuoteForFilename(nthcp);\r
6836   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6837   if (*nthdir != NULLCHAR) {\r
6838     q = QuoteForFilename(nthdir);\r
6839     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6840   }\r
6841   if (*nthcp == NULLCHAR) {\r
6842     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6843   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6844     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6845     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6846   }\r
6847 }\r
6848 \r
6849 LRESULT CALLBACK\r
6850 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6851 {\r
6852   char buf[MSG_SIZ];\r
6853   HANDLE hwndCombo;\r
6854   char *p;\r
6855 \r
6856   switch (message) {\r
6857   case WM_INITDIALOG:\r
6858     /* Center the dialog */\r
6859     CenterWindow (hDlg, GetDesktopWindow());\r
6860     /* Initialize the dialog items */\r
6861     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6862                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6863                   firstChessProgramNames);\r
6864     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6865                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6866                   secondChessProgramNames);\r
6867     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6868     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6869     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6870     if (*appData.icsHelper != NULLCHAR) {\r
6871       char *q = QuoteForFilename(appData.icsHelper);\r
6872       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6873     }\r
6874     if (*appData.icsHost == NULLCHAR) {\r
6875       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6876       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6877     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6878       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6879       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6880     }\r
6881 \r
6882     if (appData.icsActive) {\r
6883       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6884     }\r
6885     else if (appData.noChessProgram) {\r
6886       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6887     }\r
6888     else {\r
6889       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6890     }\r
6891 \r
6892     SetStartupDialogEnables(hDlg);\r
6893     return TRUE;\r
6894 \r
6895   case WM_COMMAND:\r
6896     switch (LOWORD(wParam)) {\r
6897     case IDOK:\r
6898       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6899         strcpy(buf, "/fcp=");\r
6900         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6901         p = buf;\r
6902         ParseArgs(StringGet, &p);\r
6903         strcpy(buf, "/scp=");\r
6904         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6905         p = buf;\r
6906         ParseArgs(StringGet, &p);\r
6907         appData.noChessProgram = FALSE;\r
6908         appData.icsActive = FALSE;\r
6909       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6910         strcpy(buf, "/ics /icshost=");\r
6911         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6912         p = buf;\r
6913         ParseArgs(StringGet, &p);\r
6914         if (appData.zippyPlay) {\r
6915           strcpy(buf, "/fcp=");\r
6916           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6917           p = buf;\r
6918           ParseArgs(StringGet, &p);\r
6919         }\r
6920       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6921         appData.noChessProgram = TRUE;\r
6922         appData.icsActive = FALSE;\r
6923       } else {\r
6924         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6925                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6926         return TRUE;\r
6927       }\r
6928       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6929         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6930         p = buf;\r
6931         ParseArgs(StringGet, &p);\r
6932       }\r
6933       EndDialog(hDlg, TRUE);\r
6934       return TRUE;\r
6935 \r
6936     case IDCANCEL:\r
6937       ExitEvent(0);\r
6938       return TRUE;\r
6939 \r
6940     case IDM_HELPCONTENTS:\r
6941       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6942         MessageBox (GetFocus(),\r
6943                     "Unable to activate help",\r
6944                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6945       }\r
6946       break;\r
6947 \r
6948     default:\r
6949       SetStartupDialogEnables(hDlg);\r
6950       break;\r
6951     }\r
6952     break;\r
6953   }\r
6954   return FALSE;\r
6955 }\r
6956 \r
6957 /*---------------------------------------------------------------------------*\\r
6958  *\r
6959  * About box dialog functions\r
6960  *\r
6961 \*---------------------------------------------------------------------------*/\r
6962 \r
6963 /* Process messages for "About" dialog box */\r
6964 LRESULT CALLBACK\r
6965 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6966 {\r
6967   switch (message) {\r
6968   case WM_INITDIALOG: /* message: initialize dialog box */\r
6969     /* Center the dialog over the application window */\r
6970     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6971     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6972     return (TRUE);\r
6973 \r
6974   case WM_COMMAND: /* message: received a command */\r
6975     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6976         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6977       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6978       return (TRUE);\r
6979     }\r
6980     break;\r
6981   }\r
6982   return (FALSE);\r
6983 }\r
6984 \r
6985 /*---------------------------------------------------------------------------*\\r
6986  *\r
6987  * Comment Dialog functions\r
6988  *\r
6989 \*---------------------------------------------------------------------------*/\r
6990 \r
6991 LRESULT CALLBACK\r
6992 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6993 {\r
6994   static HANDLE hwndText = NULL;\r
6995   int len, newSizeX, newSizeY, flags;\r
6996   static int sizeX, sizeY;\r
6997   char *str;\r
6998   RECT rect;\r
6999   MINMAXINFO *mmi;\r
7000 \r
7001   switch (message) {\r
7002   case WM_INITDIALOG: /* message: initialize dialog box */\r
7003     /* Initialize the dialog items */\r
7004     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7005     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7006     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7007     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7008     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7009     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7010     SetWindowText(hDlg, commentTitle);\r
7011     if (editComment) {\r
7012       SetFocus(hwndText);\r
7013     } else {\r
7014       SetFocus(GetDlgItem(hDlg, IDOK));\r
7015     }\r
7016     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7017                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7018                 MAKELPARAM(FALSE, 0));\r
7019     /* Size and position the dialog */\r
7020     if (!commentDialog) {\r
7021       commentDialog = hDlg;\r
7022       flags = SWP_NOZORDER;\r
7023       GetClientRect(hDlg, &rect);\r
7024       sizeX = rect.right;\r
7025       sizeY = rect.bottom;\r
7026       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7027           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7028         WINDOWPLACEMENT wp;\r
7029         EnsureOnScreen(&commentX, &commentY);\r
7030         wp.length = sizeof(WINDOWPLACEMENT);\r
7031         wp.flags = 0;\r
7032         wp.showCmd = SW_SHOW;\r
7033         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7034         wp.rcNormalPosition.left = commentX;\r
7035         wp.rcNormalPosition.right = commentX + commentW;\r
7036         wp.rcNormalPosition.top = commentY;\r
7037         wp.rcNormalPosition.bottom = commentY + commentH;\r
7038         SetWindowPlacement(hDlg, &wp);\r
7039 \r
7040         GetClientRect(hDlg, &rect);\r
7041         newSizeX = rect.right;\r
7042         newSizeY = rect.bottom;\r
7043         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7044                               newSizeX, newSizeY);\r
7045         sizeX = newSizeX;\r
7046         sizeY = newSizeY;\r
7047       }\r
7048     }\r
7049     return FALSE;\r
7050 \r
7051   case WM_COMMAND: /* message: received a command */\r
7052     switch (LOWORD(wParam)) {\r
7053     case IDOK:\r
7054       if (editComment) {\r
7055         char *p, *q;\r
7056         /* Read changed options from the dialog box */\r
7057         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7058         len = GetWindowTextLength(hwndText);\r
7059         str = (char *) malloc(len + 1);\r
7060         GetWindowText(hwndText, str, len + 1);\r
7061         p = q = str;\r
7062         while (*q) {\r
7063           if (*q == '\r')\r
7064             q++;\r
7065           else\r
7066             *p++ = *q++;\r
7067         }\r
7068         *p = NULLCHAR;\r
7069         ReplaceComment(commentIndex, str);\r
7070         free(str);\r
7071       }\r
7072       CommentPopDown();\r
7073       return TRUE;\r
7074 \r
7075     case IDCANCEL:\r
7076     case OPT_CancelComment:\r
7077       CommentPopDown();\r
7078       return TRUE;\r
7079 \r
7080     case OPT_ClearComment:\r
7081       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7082       break;\r
7083 \r
7084     case OPT_EditComment:\r
7085       EditCommentEvent();\r
7086       return TRUE;\r
7087 \r
7088     default:\r
7089       break;\r
7090     }\r
7091     break;\r
7092 \r
7093   case WM_SIZE:\r
7094     newSizeX = LOWORD(lParam);\r
7095     newSizeY = HIWORD(lParam);\r
7096     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7097     sizeX = newSizeX;\r
7098     sizeY = newSizeY;\r
7099     break;\r
7100 \r
7101   case WM_GETMINMAXINFO:\r
7102     /* Prevent resizing window too small */\r
7103     mmi = (MINMAXINFO *) lParam;\r
7104     mmi->ptMinTrackSize.x = 100;\r
7105     mmi->ptMinTrackSize.y = 100;\r
7106     break;\r
7107   }\r
7108   return FALSE;\r
7109 }\r
7110 \r
7111 VOID\r
7112 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7113 {\r
7114   FARPROC lpProc;\r
7115   char *p, *q;\r
7116 \r
7117   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7118 \r
7119   if (str == NULL) str = "";\r
7120   p = (char *) malloc(2 * strlen(str) + 2);\r
7121   q = p;\r
7122   while (*str) {\r
7123     if (*str == '\n') *q++ = '\r';\r
7124     *q++ = *str++;\r
7125   }\r
7126   *q = NULLCHAR;\r
7127   if (commentText != NULL) free(commentText);\r
7128 \r
7129   commentIndex = index;\r
7130   commentTitle = title;\r
7131   commentText = p;\r
7132   editComment = edit;\r
7133 \r
7134   if (commentDialog) {\r
7135     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7136     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7137   } else {\r
7138     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7139     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7140                  hwndMain, (DLGPROC)lpProc);\r
7141     FreeProcInstance(lpProc);\r
7142   }\r
7143   commentDialogUp = TRUE;\r
7144 }\r
7145 \r
7146 \r
7147 /*---------------------------------------------------------------------------*\\r
7148  *\r
7149  * Type-in move dialog functions\r
7150  * \r
7151 \*---------------------------------------------------------------------------*/\r
7152 \r
7153 LRESULT CALLBACK\r
7154 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7155 {\r
7156   char move[MSG_SIZ];\r
7157   HWND hInput;\r
7158   ChessMove moveType;\r
7159   int fromX, fromY, toX, toY;\r
7160   char promoChar;\r
7161 \r
7162   switch (message) {\r
7163   case WM_INITDIALOG:\r
7164     move[0] = (char) lParam;\r
7165     move[1] = NULLCHAR;\r
7166     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7167     hInput = GetDlgItem(hDlg, OPT_Move);\r
7168     SetWindowText(hInput, move);\r
7169     SetFocus(hInput);\r
7170     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7171     return FALSE;\r
7172 \r
7173   case WM_COMMAND:\r
7174     switch (LOWORD(wParam)) {\r
7175     case IDOK:\r
7176       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7177         gameMode != Training) {\r
7178         DisplayMoveError("Displayed move is not current");\r
7179       } else {\r
7180         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7181         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7182           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7183           if (gameMode != Training)\r
7184               forwardMostMove = currentMove;\r
7185           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7186         } else {\r
7187           DisplayMoveError("Could not parse move");\r
7188         }\r
7189       }\r
7190       EndDialog(hDlg, TRUE);\r
7191       return TRUE;\r
7192     case IDCANCEL:\r
7193       EndDialog(hDlg, FALSE);\r
7194       return TRUE;\r
7195     default:\r
7196       break;\r
7197     }\r
7198     break;\r
7199   }\r
7200   return FALSE;\r
7201 }\r
7202 \r
7203 VOID\r
7204 PopUpMoveDialog(char firstchar)\r
7205 {\r
7206     FARPROC lpProc;\r
7207     \r
7208     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7209         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7210         gameMode == AnalyzeMode || gameMode == EditGame || \r
7211         gameMode == EditPosition || gameMode == IcsExamining ||\r
7212         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7213         gameMode == Training) {\r
7214       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7215       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7216         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7217       FreeProcInstance(lpProc);\r
7218     }\r
7219 }\r
7220 \r
7221 /*---------------------------------------------------------------------------*\\r
7222  *\r
7223  * Type-in name dialog functions\r
7224  * \r
7225 \*---------------------------------------------------------------------------*/\r
7226 \r
7227 LRESULT CALLBACK\r
7228 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7229 {\r
7230   char move[MSG_SIZ];\r
7231   HWND hInput;\r
7232 \r
7233   switch (message) {\r
7234   case WM_INITDIALOG:\r
7235     move[0] = (char) lParam;\r
7236     move[1] = NULLCHAR;\r
7237     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7238     hInput = GetDlgItem(hDlg, OPT_Name);\r
7239     SetWindowText(hInput, move);\r
7240     SetFocus(hInput);\r
7241     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7242     return FALSE;\r
7243 \r
7244   case WM_COMMAND:\r
7245     switch (LOWORD(wParam)) {\r
7246     case IDOK:\r
7247       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7248       appData.userName = strdup(move);\r
7249 \r
7250       EndDialog(hDlg, TRUE);\r
7251       return TRUE;\r
7252     case IDCANCEL:\r
7253       EndDialog(hDlg, FALSE);\r
7254       return TRUE;\r
7255     default:\r
7256       break;\r
7257     }\r
7258     break;\r
7259   }\r
7260   return FALSE;\r
7261 }\r
7262 \r
7263 VOID\r
7264 PopUpNameDialog(char firstchar)\r
7265 {\r
7266     FARPROC lpProc;\r
7267     \r
7268       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7269       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7270         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7271       FreeProcInstance(lpProc);\r
7272 }\r
7273 \r
7274 /*---------------------------------------------------------------------------*\\r
7275  *\r
7276  *  Error dialogs\r
7277  * \r
7278 \*---------------------------------------------------------------------------*/\r
7279 \r
7280 /* Nonmodal error box */\r
7281 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7282                              WPARAM wParam, LPARAM lParam);\r
7283 \r
7284 VOID\r
7285 ErrorPopUp(char *title, char *content)\r
7286 {\r
7287   FARPROC lpProc;\r
7288   char *p, *q;\r
7289   BOOLEAN modal = hwndMain == NULL;\r
7290 \r
7291   p = content;\r
7292   q = errorMessage;\r
7293   while (*p) {\r
7294     if (*p == '\n') {\r
7295       if (modal) {\r
7296         *q++ = ' ';\r
7297         p++;\r
7298       } else {\r
7299         *q++ = '\r';\r
7300         *q++ = *p++;\r
7301       }\r
7302     } else {\r
7303       *q++ = *p++;\r
7304     }\r
7305   }\r
7306   *q = NULLCHAR;\r
7307   strncpy(errorTitle, title, sizeof(errorTitle));\r
7308   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7309   \r
7310   if (modal) {\r
7311     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7312   } else {\r
7313     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7314     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7315                  hwndMain, (DLGPROC)lpProc);\r
7316     FreeProcInstance(lpProc);\r
7317   }\r
7318 }\r
7319 \r
7320 VOID\r
7321 ErrorPopDown()\r
7322 {\r
7323   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7324   if (errorDialog == NULL) return;\r
7325   DestroyWindow(errorDialog);\r
7326   errorDialog = NULL;\r
7327 }\r
7328 \r
7329 LRESULT CALLBACK\r
7330 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7331 {\r
7332   HANDLE hwndText;\r
7333   RECT rChild;\r
7334 \r
7335   switch (message) {\r
7336   case WM_INITDIALOG:\r
7337     GetWindowRect(hDlg, &rChild);\r
7338 \r
7339     /*\r
7340     SetWindowPos(hDlg, NULL, rChild.left,\r
7341       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7342       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7343     */\r
7344 \r
7345     /* \r
7346         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7347         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7348         and it doesn't work when you resize the dialog.\r
7349         For now, just give it a default position.\r
7350     */\r
7351     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7352 \r
7353     errorDialog = hDlg;\r
7354     SetWindowText(hDlg, errorTitle);\r
7355     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7356     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7357     return FALSE;\r
7358 \r
7359   case WM_COMMAND:\r
7360     switch (LOWORD(wParam)) {\r
7361     case IDOK:\r
7362     case IDCANCEL:\r
7363       if (errorDialog == hDlg) errorDialog = NULL;\r
7364       DestroyWindow(hDlg);\r
7365       return TRUE;\r
7366 \r
7367     default:\r
7368       break;\r
7369     }\r
7370     break;\r
7371   }\r
7372   return FALSE;\r
7373 }\r
7374 \r
7375 #ifdef GOTHIC\r
7376 HWND gothicDialog = NULL;\r
7377 \r
7378 LRESULT CALLBACK\r
7379 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7380 {\r
7381   HANDLE hwndText;\r
7382   RECT rChild;\r
7383   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7384 \r
7385   switch (message) {\r
7386   case WM_INITDIALOG:\r
7387     GetWindowRect(hDlg, &rChild);\r
7388 \r
7389     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7390                                                              SWP_NOZORDER);\r
7391 \r
7392     /* \r
7393         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7394         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7395         and it doesn't work when you resize the dialog.\r
7396         For now, just give it a default position.\r
7397     */\r
7398     gothicDialog = hDlg;\r
7399     SetWindowText(hDlg, errorTitle);\r
7400     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7401     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7402     return FALSE;\r
7403 \r
7404   case WM_COMMAND:\r
7405     switch (LOWORD(wParam)) {\r
7406     case IDOK:\r
7407     case IDCANCEL:\r
7408       if (errorDialog == hDlg) errorDialog = NULL;\r
7409       DestroyWindow(hDlg);\r
7410       return TRUE;\r
7411 \r
7412     default:\r
7413       break;\r
7414     }\r
7415     break;\r
7416   }\r
7417   return FALSE;\r
7418 }\r
7419 \r
7420 VOID\r
7421 GothicPopUp(char *title, VariantClass variant)\r
7422 {\r
7423   FARPROC lpProc;\r
7424   static char *lastTitle;\r
7425 \r
7426   strncpy(errorTitle, title, sizeof(errorTitle));\r
7427   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7428 \r
7429   if(lastTitle != title && gothicDialog != NULL) {\r
7430     DestroyWindow(gothicDialog);\r
7431     gothicDialog = NULL;\r
7432   }\r
7433   if(variant != VariantNormal && gothicDialog == NULL) {\r
7434     title = lastTitle;\r
7435     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7436     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7437                  hwndMain, (DLGPROC)lpProc);\r
7438     FreeProcInstance(lpProc);\r
7439   }\r
7440 }\r
7441 #endif\r
7442 \r
7443 /*---------------------------------------------------------------------------*\\r
7444  *\r
7445  *  Ics Interaction console functions\r
7446  *\r
7447 \*---------------------------------------------------------------------------*/\r
7448 \r
7449 #define HISTORY_SIZE 64\r
7450 static char *history[HISTORY_SIZE];\r
7451 int histIn = 0, histP = 0;\r
7452 \r
7453 VOID\r
7454 SaveInHistory(char *cmd)\r
7455 {\r
7456   if (history[histIn] != NULL) {\r
7457     free(history[histIn]);\r
7458     history[histIn] = NULL;\r
7459   }\r
7460   if (*cmd == NULLCHAR) return;\r
7461   history[histIn] = StrSave(cmd);\r
7462   histIn = (histIn + 1) % HISTORY_SIZE;\r
7463   if (history[histIn] != NULL) {\r
7464     free(history[histIn]);\r
7465     history[histIn] = NULL;\r
7466   }\r
7467   histP = histIn;\r
7468 }\r
7469 \r
7470 char *\r
7471 PrevInHistory(char *cmd)\r
7472 {\r
7473   int newhp;\r
7474   if (histP == histIn) {\r
7475     if (history[histIn] != NULL) free(history[histIn]);\r
7476     history[histIn] = StrSave(cmd);\r
7477   }\r
7478   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7479   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7480   histP = newhp;\r
7481   return history[histP];\r
7482 }\r
7483 \r
7484 char *\r
7485 NextInHistory()\r
7486 {\r
7487   if (histP == histIn) return NULL;\r
7488   histP = (histP + 1) % HISTORY_SIZE;\r
7489   return history[histP];\r
7490 }\r
7491 \r
7492 typedef struct {\r
7493   char *item;\r
7494   char *command;\r
7495   BOOLEAN getname;\r
7496   BOOLEAN immediate;\r
7497 } IcsTextMenuEntry;\r
7498 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7499 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7500 \r
7501 void\r
7502 ParseIcsTextMenu(char *icsTextMenuString)\r
7503 {\r
7504 //  int flags = 0;\r
7505   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7506   char *p = icsTextMenuString;\r
7507   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7508     free(e->item);\r
7509     e->item = NULL;\r
7510     if (e->command != NULL) {\r
7511       free(e->command);\r
7512       e->command = NULL;\r
7513     }\r
7514     e++;\r
7515   }\r
7516   e = icsTextMenuEntry;\r
7517   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7518     if (*p == ';' || *p == '\n') {\r
7519       e->item = strdup("-");\r
7520       e->command = NULL;\r
7521       p++;\r
7522     } else if (*p == '-') {\r
7523       e->item = strdup("-");\r
7524       e->command = NULL;\r
7525       p++;\r
7526       if (*p) p++;\r
7527     } else {\r
7528       char *q, *r, *s, *t;\r
7529       char c;\r
7530       q = strchr(p, ',');\r
7531       if (q == NULL) break;\r
7532       *q = NULLCHAR;\r
7533       r = strchr(q + 1, ',');\r
7534       if (r == NULL) break;\r
7535       *r = NULLCHAR;\r
7536       s = strchr(r + 1, ',');\r
7537       if (s == NULL) break;\r
7538       *s = NULLCHAR;\r
7539       c = ';';\r
7540       t = strchr(s + 1, c);\r
7541       if (t == NULL) {\r
7542         c = '\n';\r
7543         t = strchr(s + 1, c);\r
7544       }\r
7545       if (t != NULL) *t = NULLCHAR;\r
7546       e->item = strdup(p);\r
7547       e->command = strdup(q + 1);\r
7548       e->getname = *(r + 1) != '0';\r
7549       e->immediate = *(s + 1) != '0';\r
7550       *q = ',';\r
7551       *r = ',';\r
7552       *s = ',';\r
7553       if (t == NULL) break;\r
7554       *t = c;\r
7555       p = t + 1;\r
7556     }\r
7557     e++;\r
7558   } \r
7559 }\r
7560 \r
7561 HMENU\r
7562 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7563 {\r
7564   HMENU hmenu, h;\r
7565   int i = 0;\r
7566   hmenu = LoadMenu(hInst, "TextMenu");\r
7567   h = GetSubMenu(hmenu, 0);\r
7568   while (e->item) {\r
7569     if (strcmp(e->item, "-") == 0) {\r
7570       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7571     } else {\r
7572       if (e->item[0] == '|') {\r
7573         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7574                    IDM_CommandX + i, &e->item[1]);\r
7575       } else {\r
7576         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7577       }\r
7578     }\r
7579     e++;\r
7580     i++;\r
7581   } \r
7582   return hmenu;\r
7583 }\r
7584 \r
7585 WNDPROC consoleTextWindowProc;\r
7586 \r
7587 void\r
7588 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7589 {\r
7590   char buf[MSG_SIZ], name[MSG_SIZ];\r
7591   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7592   CHARRANGE sel;\r
7593 \r
7594   if (!getname) {\r
7595     SetWindowText(hInput, command);\r
7596     if (immediate) {\r
7597       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7598     } else {\r
7599       sel.cpMin = 999999;\r
7600       sel.cpMax = 999999;\r
7601       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7602       SetFocus(hInput);\r
7603     }\r
7604     return;\r
7605   }    \r
7606   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7607   if (sel.cpMin == sel.cpMax) {\r
7608     /* Expand to surrounding word */\r
7609     TEXTRANGE tr;\r
7610     do {\r
7611       tr.chrg.cpMax = sel.cpMin;\r
7612       tr.chrg.cpMin = --sel.cpMin;\r
7613       if (sel.cpMin < 0) break;\r
7614       tr.lpstrText = name;\r
7615       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7616     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7617     sel.cpMin++;\r
7618 \r
7619     do {\r
7620       tr.chrg.cpMin = sel.cpMax;\r
7621       tr.chrg.cpMax = ++sel.cpMax;\r
7622       tr.lpstrText = name;\r
7623       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7624     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7625     sel.cpMax--;\r
7626 \r
7627     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7628       MessageBeep(MB_ICONEXCLAMATION);\r
7629       return;\r
7630     }\r
7631     tr.chrg = sel;\r
7632     tr.lpstrText = name;\r
7633     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7634   } else {\r
7635     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7636       MessageBeep(MB_ICONEXCLAMATION);\r
7637       return;\r
7638     }\r
7639     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7640   }\r
7641   if (immediate) {\r
7642     sprintf(buf, "%s %s", command, name);\r
7643     SetWindowText(hInput, buf);\r
7644     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7645   } else {\r
7646     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7647     SetWindowText(hInput, buf);\r
7648     sel.cpMin = 999999;\r
7649     sel.cpMax = 999999;\r
7650     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7651     SetFocus(hInput);\r
7652   }\r
7653 }\r
7654 \r
7655 LRESULT CALLBACK \r
7656 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7657 {\r
7658   HWND hInput;\r
7659   CHARRANGE sel;\r
7660 \r
7661   switch (message) {\r
7662   case WM_KEYDOWN:\r
7663     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7664     switch (wParam) {\r
7665     case VK_PRIOR:\r
7666       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7667       return 0;\r
7668     case VK_NEXT:\r
7669       sel.cpMin = 999999;\r
7670       sel.cpMax = 999999;\r
7671       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7672       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7673       return 0;\r
7674     }\r
7675     break;\r
7676   case WM_CHAR:\r
7677     if (wParam == '\t') {\r
7678       if (GetKeyState(VK_SHIFT) < 0) {\r
7679         /* shifted */\r
7680         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7681         if (buttonDesc[0].hwnd) {\r
7682           SetFocus(buttonDesc[0].hwnd);\r
7683         } else {\r
7684           SetFocus(hwndMain);\r
7685         }\r
7686       } else {\r
7687         /* unshifted */\r
7688         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7689       }\r
7690     } else {\r
7691       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7692       SetFocus(hInput);\r
7693       SendMessage(hInput, message, wParam, lParam);\r
7694     }\r
7695     return 0;\r
7696   case WM_PASTE:\r
7697     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7698     SetFocus(hInput);\r
7699     return SendMessage(hInput, message, wParam, lParam);\r
7700   case WM_MBUTTONDOWN:\r
7701     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7702   case WM_RBUTTONDOWN:\r
7703     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7704       /* Move selection here if it was empty */\r
7705       POINT pt;\r
7706       pt.x = LOWORD(lParam);\r
7707       pt.y = HIWORD(lParam);\r
7708       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7709       if (sel.cpMin == sel.cpMax) {\r
7710         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7711         sel.cpMax = sel.cpMin;\r
7712         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7713       }\r
7714       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7715     }\r
7716     return 0;\r
7717   case WM_RBUTTONUP:\r
7718     if (GetKeyState(VK_SHIFT) & ~1) {\r
7719       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7720         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7721     } else {\r
7722       POINT pt;\r
7723       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7724       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7725       if (sel.cpMin == sel.cpMax) {\r
7726         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7727         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7728       }\r
7729       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7730         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7731       }\r
7732       pt.x = LOWORD(lParam);\r
7733       pt.y = HIWORD(lParam);\r
7734       MenuPopup(hwnd, pt, hmenu, -1);\r
7735     }\r
7736     return 0;\r
7737   case WM_COMMAND:\r
7738     switch (LOWORD(wParam)) {\r
7739     case IDM_QuickPaste:\r
7740       {\r
7741         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7742         if (sel.cpMin == sel.cpMax) {\r
7743           MessageBeep(MB_ICONEXCLAMATION);\r
7744           return 0;\r
7745         }\r
7746         SendMessage(hwnd, WM_COPY, 0, 0);\r
7747         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7748         SendMessage(hInput, WM_PASTE, 0, 0);\r
7749         SetFocus(hInput);\r
7750         return 0;\r
7751       }\r
7752     case IDM_Cut:\r
7753       SendMessage(hwnd, WM_CUT, 0, 0);\r
7754       return 0;\r
7755     case IDM_Paste:\r
7756       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7757       return 0;\r
7758     case IDM_Copy:\r
7759       SendMessage(hwnd, WM_COPY, 0, 0);\r
7760       return 0;\r
7761     default:\r
7762       {\r
7763         int i = LOWORD(wParam) - IDM_CommandX;\r
7764         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7765             icsTextMenuEntry[i].command != NULL) {\r
7766           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7767                    icsTextMenuEntry[i].getname,\r
7768                    icsTextMenuEntry[i].immediate);\r
7769           return 0;\r
7770         }\r
7771       }\r
7772       break;\r
7773     }\r
7774     break;\r
7775   }\r
7776   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7777 }\r
7778 \r
7779 WNDPROC consoleInputWindowProc;\r
7780 \r
7781 LRESULT CALLBACK\r
7782 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7783 {\r
7784   char buf[MSG_SIZ];\r
7785   char *p;\r
7786   static BOOL sendNextChar = FALSE;\r
7787   static BOOL quoteNextChar = FALSE;\r
7788   InputSource *is = consoleInputSource;\r
7789   CHARFORMAT cf;\r
7790   CHARRANGE sel;\r
7791 \r
7792   switch (message) {\r
7793   case WM_CHAR:\r
7794     if (!appData.localLineEditing || sendNextChar) {\r
7795       is->buf[0] = (CHAR) wParam;\r
7796       is->count = 1;\r
7797       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7798       sendNextChar = FALSE;\r
7799       return 0;\r
7800     }\r
7801     if (quoteNextChar) {\r
7802       buf[0] = (char) wParam;\r
7803       buf[1] = NULLCHAR;\r
7804       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7805       quoteNextChar = FALSE;\r
7806       return 0;\r
7807     }\r
7808     switch (wParam) {\r
7809     case '\r':   /* Enter key */\r
7810       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7811       if (consoleEcho) SaveInHistory(is->buf);\r
7812       is->buf[is->count++] = '\n';\r
7813       is->buf[is->count] = NULLCHAR;\r
7814       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7815       if (consoleEcho) {\r
7816         ConsoleOutput(is->buf, is->count, TRUE);\r
7817       } else if (appData.localLineEditing) {\r
7818         ConsoleOutput("\n", 1, TRUE);\r
7819       }\r
7820       /* fall thru */\r
7821     case '\033': /* Escape key */\r
7822       SetWindowText(hwnd, "");\r
7823       cf.cbSize = sizeof(CHARFORMAT);\r
7824       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7825       if (consoleEcho) {\r
7826         cf.crTextColor = textAttribs[ColorNormal].color;\r
7827       } else {\r
7828         cf.crTextColor = COLOR_ECHOOFF;\r
7829       }\r
7830       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7831       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7832       return 0;\r
7833     case '\t':   /* Tab key */\r
7834       if (GetKeyState(VK_SHIFT) < 0) {\r
7835         /* shifted */\r
7836         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7837       } else {\r
7838         /* unshifted */\r
7839         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7840         if (buttonDesc[0].hwnd) {\r
7841           SetFocus(buttonDesc[0].hwnd);\r
7842         } else {\r
7843           SetFocus(hwndMain);\r
7844         }\r
7845       }\r
7846       return 0;\r
7847     case '\023': /* Ctrl+S */\r
7848       sendNextChar = TRUE;\r
7849       return 0;\r
7850     case '\021': /* Ctrl+Q */\r
7851       quoteNextChar = TRUE;\r
7852       return 0;\r
7853     default:\r
7854       break;\r
7855     }\r
7856     break;\r
7857   case WM_KEYDOWN:\r
7858     switch (wParam) {\r
7859     case VK_UP:\r
7860       GetWindowText(hwnd, buf, MSG_SIZ);\r
7861       p = PrevInHistory(buf);\r
7862       if (p != NULL) {\r
7863         SetWindowText(hwnd, p);\r
7864         sel.cpMin = 999999;\r
7865         sel.cpMax = 999999;\r
7866         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7867         return 0;\r
7868       }\r
7869       break;\r
7870     case VK_DOWN:\r
7871       p = NextInHistory();\r
7872       if (p != NULL) {\r
7873         SetWindowText(hwnd, p);\r
7874         sel.cpMin = 999999;\r
7875         sel.cpMax = 999999;\r
7876         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7877         return 0;\r
7878       }\r
7879       break;\r
7880     case VK_HOME:\r
7881     case VK_END:\r
7882       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7883       /* fall thru */\r
7884     case VK_PRIOR:\r
7885     case VK_NEXT:\r
7886       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7887       return 0;\r
7888     }\r
7889     break;\r
7890   case WM_MBUTTONDOWN:\r
7891     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7892       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7893     break;\r
7894   case WM_RBUTTONUP:\r
7895     if (GetKeyState(VK_SHIFT) & ~1) {\r
7896       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7897         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7898     } else {\r
7899       POINT pt;\r
7900       HMENU hmenu;\r
7901       hmenu = LoadMenu(hInst, "InputMenu");\r
7902       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7903       if (sel.cpMin == sel.cpMax) {\r
7904         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7905         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7906       }\r
7907       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7908         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7909       }\r
7910       pt.x = LOWORD(lParam);\r
7911       pt.y = HIWORD(lParam);\r
7912       MenuPopup(hwnd, pt, hmenu, -1);\r
7913     }\r
7914     return 0;\r
7915   case WM_COMMAND:\r
7916     switch (LOWORD(wParam)) { \r
7917     case IDM_Undo:\r
7918       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7919       return 0;\r
7920     case IDM_SelectAll:\r
7921       sel.cpMin = 0;\r
7922       sel.cpMax = -1; /*999999?*/\r
7923       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7924       return 0;\r
7925     case IDM_Cut:\r
7926       SendMessage(hwnd, WM_CUT, 0, 0);\r
7927       return 0;\r
7928     case IDM_Paste:\r
7929       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7930       return 0;\r
7931     case IDM_Copy:\r
7932       SendMessage(hwnd, WM_COPY, 0, 0);\r
7933       return 0;\r
7934     }\r
7935     break;\r
7936   }\r
7937   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7938 }\r
7939 \r
7940 #define CO_MAX  100000\r
7941 #define CO_TRIM   1000\r
7942 \r
7943 LRESULT CALLBACK\r
7944 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7945 {\r
7946   static SnapData sd;\r
7947   static HWND hText, hInput /*, hFocus*/;\r
7948 //  InputSource *is = consoleInputSource;\r
7949   RECT rect;\r
7950   static int sizeX, sizeY;\r
7951   int newSizeX, newSizeY;\r
7952   MINMAXINFO *mmi;\r
7953 \r
7954   switch (message) {\r
7955   case WM_INITDIALOG: /* message: initialize dialog box */\r
7956     hwndConsole = hDlg;\r
7957     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7958     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7959     SetFocus(hInput);\r
7960     consoleTextWindowProc = (WNDPROC)\r
7961       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7962     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7963     consoleInputWindowProc = (WNDPROC)\r
7964       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7965     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7966     Colorize(ColorNormal, TRUE);\r
7967     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7968     ChangedConsoleFont();\r
7969     GetClientRect(hDlg, &rect);\r
7970     sizeX = rect.right;\r
7971     sizeY = rect.bottom;\r
7972     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7973         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7974       WINDOWPLACEMENT wp;\r
7975       EnsureOnScreen(&consoleX, &consoleY);\r
7976       wp.length = sizeof(WINDOWPLACEMENT);\r
7977       wp.flags = 0;\r
7978       wp.showCmd = SW_SHOW;\r
7979       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7980       wp.rcNormalPosition.left = consoleX;\r
7981       wp.rcNormalPosition.right = consoleX + consoleW;\r
7982       wp.rcNormalPosition.top = consoleY;\r
7983       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7984       SetWindowPlacement(hDlg, &wp);\r
7985     }\r
7986 #if 0 \r
7987    // [HGM] Chessknight's change 2004-07-13\r
7988    else { /* Determine Defaults */\r
7989        WINDOWPLACEMENT wp;\r
7990        consoleX = winWidth + 1;\r
7991        consoleY = boardY;\r
7992        consoleW = screenWidth -  winWidth;\r
7993        consoleH = winHeight;\r
7994        EnsureOnScreen(&consoleX, &consoleY);\r
7995        wp.length = sizeof(WINDOWPLACEMENT);\r
7996        wp.flags = 0;\r
7997        wp.showCmd = SW_SHOW;\r
7998        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7999        wp.rcNormalPosition.left = consoleX;\r
8000        wp.rcNormalPosition.right = consoleX + consoleW;\r
8001        wp.rcNormalPosition.top = consoleY;\r
8002        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8003        SetWindowPlacement(hDlg, &wp);\r
8004     }\r
8005 #endif\r
8006     return FALSE;\r
8007 \r
8008   case WM_SETFOCUS:\r
8009     SetFocus(hInput);\r
8010     return 0;\r
8011 \r
8012   case WM_CLOSE:\r
8013     ExitEvent(0);\r
8014     /* not reached */\r
8015     break;\r
8016 \r
8017   case WM_SIZE:\r
8018     if (IsIconic(hDlg)) break;\r
8019     newSizeX = LOWORD(lParam);\r
8020     newSizeY = HIWORD(lParam);\r
8021     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8022       RECT rectText, rectInput;\r
8023       POINT pt;\r
8024       int newTextHeight, newTextWidth;\r
8025       GetWindowRect(hText, &rectText);\r
8026       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8027       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8028       if (newTextHeight < 0) {\r
8029         newSizeY += -newTextHeight;\r
8030         newTextHeight = 0;\r
8031       }\r
8032       SetWindowPos(hText, NULL, 0, 0,\r
8033         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8034       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8035       pt.x = rectInput.left;\r
8036       pt.y = rectInput.top + newSizeY - sizeY;\r
8037       ScreenToClient(hDlg, &pt);\r
8038       SetWindowPos(hInput, NULL, \r
8039         pt.x, pt.y, /* needs client coords */   \r
8040         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8041         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8042     }\r
8043     sizeX = newSizeX;\r
8044     sizeY = newSizeY;\r
8045     break;\r
8046 \r
8047   case WM_GETMINMAXINFO:\r
8048     /* Prevent resizing window too small */\r
8049     mmi = (MINMAXINFO *) lParam;\r
8050     mmi->ptMinTrackSize.x = 100;\r
8051     mmi->ptMinTrackSize.y = 100;\r
8052     break;\r
8053 \r
8054   /* [AS] Snapping */\r
8055   case WM_ENTERSIZEMOVE:\r
8056     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8057 \r
8058   case WM_SIZING:\r
8059     return OnSizing( &sd, hDlg, wParam, lParam );\r
8060 \r
8061   case WM_MOVING:\r
8062     return OnMoving( &sd, hDlg, wParam, lParam );\r
8063 \r
8064   case WM_EXITSIZEMOVE:\r
8065     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8066   }\r
8067 \r
8068   return DefWindowProc(hDlg, message, wParam, lParam);\r
8069 }\r
8070 \r
8071 \r
8072 VOID\r
8073 ConsoleCreate()\r
8074 {\r
8075   HWND hCons;\r
8076   if (hwndConsole) return;\r
8077   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8078   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8079 }\r
8080 \r
8081 \r
8082 VOID\r
8083 ConsoleOutput(char* data, int length, int forceVisible)\r
8084 {\r
8085   HWND hText;\r
8086   int trim, exlen;\r
8087   char *p, *q;\r
8088   char buf[CO_MAX+1];\r
8089   POINT pEnd;\r
8090   RECT rect;\r
8091   static int delayLF = 0;\r
8092   CHARRANGE savesel, sel;\r
8093 \r
8094   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8095   p = data;\r
8096   q = buf;\r
8097   if (delayLF) {\r
8098     *q++ = '\r';\r
8099     *q++ = '\n';\r
8100     delayLF = 0;\r
8101   }\r
8102   while (length--) {\r
8103     if (*p == '\n') {\r
8104       if (*++p) {\r
8105         *q++ = '\r';\r
8106         *q++ = '\n';\r
8107       } else {\r
8108         delayLF = 1;\r
8109       }\r
8110     } else if (*p == '\007') {\r
8111        MyPlaySound(&sounds[(int)SoundBell]);\r
8112        p++;\r
8113     } else {\r
8114       *q++ = *p++;\r
8115     }\r
8116   }\r
8117   *q = NULLCHAR;\r
8118   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8119   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8120   /* Save current selection */\r
8121   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8122   exlen = GetWindowTextLength(hText);\r
8123   /* Find out whether current end of text is visible */\r
8124   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8125   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8126   /* Trim existing text if it's too long */\r
8127   if (exlen + (q - buf) > CO_MAX) {\r
8128     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8129     sel.cpMin = 0;\r
8130     sel.cpMax = trim;\r
8131     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8132     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8133     exlen -= trim;\r
8134     savesel.cpMin -= trim;\r
8135     savesel.cpMax -= trim;\r
8136     if (exlen < 0) exlen = 0;\r
8137     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8138     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8139   }\r
8140   /* Append the new text */\r
8141   sel.cpMin = exlen;\r
8142   sel.cpMax = exlen;\r
8143   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8144   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8145   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8146   if (forceVisible || exlen == 0 ||\r
8147       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8148        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8149     /* Scroll to make new end of text visible if old end of text\r
8150        was visible or new text is an echo of user typein */\r
8151     sel.cpMin = 9999999;\r
8152     sel.cpMax = 9999999;\r
8153     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8154     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8155     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8156     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8157   }\r
8158   if (savesel.cpMax == exlen || forceVisible) {\r
8159     /* Move insert point to new end of text if it was at the old\r
8160        end of text or if the new text is an echo of user typein */\r
8161     sel.cpMin = 9999999;\r
8162     sel.cpMax = 9999999;\r
8163     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8164   } else {\r
8165     /* Restore previous selection */\r
8166     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8167   }\r
8168   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8169 }\r
8170 \r
8171 /*---------*/\r
8172 \r
8173 \r
8174 void\r
8175 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8176 {\r
8177   char buf[100];\r
8178   char *str;\r
8179   COLORREF oldFg, oldBg;\r
8180   HFONT oldFont;\r
8181   RECT rect;\r
8182 \r
8183   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8184 \r
8185   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8186   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8187   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8188 \r
8189   rect.left = x;\r
8190   rect.right = x + squareSize;\r
8191   rect.top  = y;\r
8192   rect.bottom = y + squareSize;\r
8193   str = buf;\r
8194 \r
8195   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8196                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8197              y, ETO_CLIPPED|ETO_OPAQUE,\r
8198              &rect, str, strlen(str), NULL);\r
8199 \r
8200   (void) SetTextColor(hdc, oldFg);\r
8201   (void) SetBkColor(hdc, oldBg);\r
8202   (void) SelectObject(hdc, oldFont);\r
8203 }\r
8204 \r
8205 void\r
8206 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8207               RECT *rect, char *color, char *flagFell)\r
8208 {\r
8209   char buf[100];\r
8210   char *str;\r
8211   COLORREF oldFg, oldBg;\r
8212   HFONT oldFont;\r
8213 \r
8214   if (appData.clockMode) {\r
8215     if (tinyLayout)\r
8216       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8217     else\r
8218       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8219     str = buf;\r
8220   } else {\r
8221     str = color;\r
8222   }\r
8223 \r
8224   if (highlight) {\r
8225     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8226     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8227   } else {\r
8228     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8229     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8230   }\r
8231   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8232 \r
8233   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8234              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8235              rect, str, strlen(str), NULL);\r
8236 \r
8237   (void) SetTextColor(hdc, oldFg);\r
8238   (void) SetBkColor(hdc, oldBg);\r
8239   (void) SelectObject(hdc, oldFont);\r
8240 }\r
8241 \r
8242 \r
8243 int\r
8244 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8245            OVERLAPPED *ovl)\r
8246 {\r
8247   int ok, err;\r
8248 \r
8249   /* [AS]  */\r
8250   if( count <= 0 ) {\r
8251     if (appData.debugMode) {\r
8252       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8253     }\r
8254 \r
8255     return ERROR_INVALID_USER_BUFFER;\r
8256   }\r
8257 \r
8258   ResetEvent(ovl->hEvent);\r
8259   ovl->Offset = ovl->OffsetHigh = 0;\r
8260   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8261   if (ok) {\r
8262     err = NO_ERROR;\r
8263   } else {\r
8264     err = GetLastError();\r
8265     if (err == ERROR_IO_PENDING) {\r
8266       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8267       if (ok)\r
8268         err = NO_ERROR;\r
8269       else\r
8270         err = GetLastError();\r
8271     }\r
8272   }\r
8273   return err;\r
8274 }\r
8275 \r
8276 int\r
8277 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8278             OVERLAPPED *ovl)\r
8279 {\r
8280   int ok, err;\r
8281 \r
8282   ResetEvent(ovl->hEvent);\r
8283   ovl->Offset = ovl->OffsetHigh = 0;\r
8284   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8285   if (ok) {\r
8286     err = NO_ERROR;\r
8287   } else {\r
8288     err = GetLastError();\r
8289     if (err == ERROR_IO_PENDING) {\r
8290       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8291       if (ok)\r
8292         err = NO_ERROR;\r
8293       else\r
8294         err = GetLastError();\r
8295     }\r
8296   }\r
8297   return err;\r
8298 }\r
8299 \r
8300 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8301 void CheckForInputBufferFull( InputSource * is )\r
8302 {\r
8303     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8304         /* Look for end of line */\r
8305         char * p = is->buf;\r
8306         \r
8307         while( p < is->next && *p != '\n' ) {\r
8308             p++;\r
8309         }\r
8310 \r
8311         if( p >= is->next ) {\r
8312             if (appData.debugMode) {\r
8313                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8314             }\r
8315 \r
8316             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8317             is->count = (DWORD) -1;\r
8318             is->next = is->buf;\r
8319         }\r
8320     }\r
8321 }\r
8322 \r
8323 DWORD\r
8324 InputThread(LPVOID arg)\r
8325 {\r
8326   InputSource *is;\r
8327   OVERLAPPED ovl;\r
8328 \r
8329   is = (InputSource *) arg;\r
8330   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8331   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8332   while (is->hThread != NULL) {\r
8333     is->error = DoReadFile(is->hFile, is->next,\r
8334                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8335                            &is->count, &ovl);\r
8336     if (is->error == NO_ERROR) {\r
8337       is->next += is->count;\r
8338     } else {\r
8339       if (is->error == ERROR_BROKEN_PIPE) {\r
8340         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8341         is->count = 0;\r
8342       } else {\r
8343         is->count = (DWORD) -1;\r
8344         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8345         break; \r
8346       }\r
8347     }\r
8348 \r
8349     CheckForInputBufferFull( is );\r
8350 \r
8351     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8352 \r
8353     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8354 \r
8355     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8356   }\r
8357 \r
8358   CloseHandle(ovl.hEvent);\r
8359   CloseHandle(is->hFile);\r
8360 \r
8361   if (appData.debugMode) {\r
8362     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8363   }\r
8364 \r
8365   return 0;\r
8366 }\r
8367 \r
8368 \r
8369 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8370 DWORD\r
8371 NonOvlInputThread(LPVOID arg)\r
8372 {\r
8373   InputSource *is;\r
8374   char *p, *q;\r
8375   int i;\r
8376   char prev;\r
8377 \r
8378   is = (InputSource *) arg;\r
8379   while (is->hThread != NULL) {\r
8380     is->error = ReadFile(is->hFile, is->next,\r
8381                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8382                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8383     if (is->error == NO_ERROR) {\r
8384       /* Change CRLF to LF */\r
8385       if (is->next > is->buf) {\r
8386         p = is->next - 1;\r
8387         i = is->count + 1;\r
8388       } else {\r
8389         p = is->next;\r
8390         i = is->count;\r
8391       }\r
8392       q = p;\r
8393       prev = NULLCHAR;\r
8394       while (i > 0) {\r
8395         if (prev == '\r' && *p == '\n') {\r
8396           *(q-1) = '\n';\r
8397           is->count--;\r
8398         } else { \r
8399           *q++ = *p;\r
8400         }\r
8401         prev = *p++;\r
8402         i--;\r
8403       }\r
8404       *q = NULLCHAR;\r
8405       is->next = q;\r
8406     } else {\r
8407       if (is->error == ERROR_BROKEN_PIPE) {\r
8408         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8409         is->count = 0; \r
8410       } else {\r
8411         is->count = (DWORD) -1;\r
8412       }\r
8413     }\r
8414 \r
8415     CheckForInputBufferFull( is );\r
8416 \r
8417     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8418 \r
8419     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8420 \r
8421     if (is->count < 0) break;  /* Quit on error */\r
8422   }\r
8423   CloseHandle(is->hFile);\r
8424   return 0;\r
8425 }\r
8426 \r
8427 DWORD\r
8428 SocketInputThread(LPVOID arg)\r
8429 {\r
8430   InputSource *is;\r
8431 \r
8432   is = (InputSource *) arg;\r
8433   while (is->hThread != NULL) {\r
8434     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8435     if ((int)is->count == SOCKET_ERROR) {\r
8436       is->count = (DWORD) -1;\r
8437       is->error = WSAGetLastError();\r
8438     } else {\r
8439       is->error = NO_ERROR;\r
8440       is->next += is->count;\r
8441       if (is->count == 0 && is->second == is) {\r
8442         /* End of file on stderr; quit with no message */\r
8443         break;\r
8444       }\r
8445     }\r
8446     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8447 \r
8448     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8449 \r
8450     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8451   }\r
8452   return 0;\r
8453 }\r
8454 \r
8455 VOID\r
8456 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8457 {\r
8458   InputSource *is;\r
8459 \r
8460   is = (InputSource *) lParam;\r
8461   if (is->lineByLine) {\r
8462     /* Feed in lines one by one */\r
8463     char *p = is->buf;\r
8464     char *q = p;\r
8465     while (q < is->next) {\r
8466       if (*q++ == '\n') {\r
8467         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8468         p = q;\r
8469       }\r
8470     }\r
8471     \r
8472     /* Move any partial line to the start of the buffer */\r
8473     q = is->buf;\r
8474     while (p < is->next) {\r
8475       *q++ = *p++;\r
8476     }\r
8477     is->next = q;\r
8478 \r
8479     if (is->error != NO_ERROR || is->count == 0) {\r
8480       /* Notify backend of the error.  Note: If there was a partial\r
8481          line at the end, it is not flushed through. */\r
8482       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8483     }\r
8484   } else {\r
8485     /* Feed in the whole chunk of input at once */\r
8486     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8487     is->next = is->buf;\r
8488   }\r
8489 }\r
8490 \r
8491 /*---------------------------------------------------------------------------*\\r
8492  *\r
8493  *  Menu enables. Used when setting various modes.\r
8494  *\r
8495 \*---------------------------------------------------------------------------*/\r
8496 \r
8497 typedef struct {\r
8498   int item;\r
8499   int flags;\r
8500 } Enables;\r
8501 \r
8502 VOID\r
8503 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8504 {\r
8505   while (enab->item > 0) {\r
8506     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8507     enab++;\r
8508   }\r
8509 }\r
8510 \r
8511 Enables gnuEnables[] = {\r
8512   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8516   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8517   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8518   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8519   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8520   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8521   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8522   { -1, -1 }\r
8523 };\r
8524 \r
8525 Enables icsEnables[] = {\r
8526   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8529   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8531   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8532   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8533   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8534   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8535   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8536   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8537   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8538   { -1, -1 }\r
8539 };\r
8540 \r
8541 #ifdef ZIPPY\r
8542 Enables zippyEnables[] = {\r
8543   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8544   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8545   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8546   { -1, -1 }\r
8547 };\r
8548 #endif\r
8549 \r
8550 Enables ncpEnables[] = {\r
8551   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8559   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8560   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8566   { -1, -1 }\r
8567 };\r
8568 \r
8569 Enables trainingOnEnables[] = {\r
8570   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8578   { -1, -1 }\r
8579 };\r
8580 \r
8581 Enables trainingOffEnables[] = {\r
8582   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8583   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8584   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8585   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8586   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8588   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8589   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8590   { -1, -1 }\r
8591 };\r
8592 \r
8593 /* These modify either ncpEnables or gnuEnables */\r
8594 Enables cmailEnables[] = {\r
8595   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8596   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8597   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8598   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8600   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8602   { -1, -1 }\r
8603 };\r
8604 \r
8605 Enables machineThinkingEnables[] = {\r
8606   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8620   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8621   { -1, -1 }\r
8622 };\r
8623 \r
8624 Enables userThinkingEnables[] = {\r
8625   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8633   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8635   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8636   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8637   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8640   { -1, -1 }\r
8641 };\r
8642 \r
8643 /*---------------------------------------------------------------------------*\\r
8644  *\r
8645  *  Front-end interface functions exported by XBoard.\r
8646  *  Functions appear in same order as prototypes in frontend.h.\r
8647  * \r
8648 \*---------------------------------------------------------------------------*/\r
8649 VOID\r
8650 ModeHighlight()\r
8651 {\r
8652   static UINT prevChecked = 0;\r
8653   static int prevPausing = 0;\r
8654   UINT nowChecked;\r
8655 \r
8656   if (pausing != prevPausing) {\r
8657     prevPausing = pausing;\r
8658     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8659                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8660     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8661   }\r
8662 \r
8663   switch (gameMode) {\r
8664   case BeginningOfGame:\r
8665     if (appData.icsActive)\r
8666       nowChecked = IDM_IcsClient;\r
8667     else if (appData.noChessProgram)\r
8668       nowChecked = IDM_EditGame;\r
8669     else\r
8670       nowChecked = IDM_MachineBlack;\r
8671     break;\r
8672   case MachinePlaysBlack:\r
8673     nowChecked = IDM_MachineBlack;\r
8674     break;\r
8675   case MachinePlaysWhite:\r
8676     nowChecked = IDM_MachineWhite;\r
8677     break;\r
8678   case TwoMachinesPlay:\r
8679     nowChecked = IDM_TwoMachines;\r
8680     break;\r
8681   case AnalyzeMode:\r
8682     nowChecked = IDM_AnalysisMode;\r
8683     break;\r
8684   case AnalyzeFile:\r
8685     nowChecked = IDM_AnalyzeFile;\r
8686     break;\r
8687   case EditGame:\r
8688     nowChecked = IDM_EditGame;\r
8689     break;\r
8690   case PlayFromGameFile:\r
8691     nowChecked = IDM_LoadGame;\r
8692     break;\r
8693   case EditPosition:\r
8694     nowChecked = IDM_EditPosition;\r
8695     break;\r
8696   case Training:\r
8697     nowChecked = IDM_Training;\r
8698     break;\r
8699   case IcsPlayingWhite:\r
8700   case IcsPlayingBlack:\r
8701   case IcsObserving:\r
8702   case IcsIdle:\r
8703     nowChecked = IDM_IcsClient;\r
8704     break;\r
8705   default:\r
8706   case EndOfGame:\r
8707     nowChecked = 0;\r
8708     break;\r
8709   }\r
8710   if (prevChecked != 0)\r
8711     (void) CheckMenuItem(GetMenu(hwndMain),\r
8712                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8713   if (nowChecked != 0)\r
8714     (void) CheckMenuItem(GetMenu(hwndMain),\r
8715                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8716 \r
8717   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8718     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8719                           MF_BYCOMMAND|MF_ENABLED);\r
8720   } else {\r
8721     (void) EnableMenuItem(GetMenu(hwndMain), \r
8722                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8723   }\r
8724 \r
8725   prevChecked = nowChecked;\r
8726 \r
8727   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8728   if (appData.icsActive) {\r
8729        if (appData.icsEngineAnalyze) {\r
8730                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8731                        MF_BYCOMMAND|MF_CHECKED);\r
8732        } else {\r
8733                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8734                        MF_BYCOMMAND|MF_UNCHECKED);\r
8735        }\r
8736   }\r
8737 }\r
8738 \r
8739 VOID\r
8740 SetICSMode()\r
8741 {\r
8742   HMENU hmenu = GetMenu(hwndMain);\r
8743   SetMenuEnables(hmenu, icsEnables);\r
8744   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8745     MF_BYPOSITION|MF_ENABLED);\r
8746 #ifdef ZIPPY\r
8747   if (appData.zippyPlay) {\r
8748     SetMenuEnables(hmenu, zippyEnables);\r
8749     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8750          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8751           MF_BYCOMMAND|MF_ENABLED);\r
8752   }\r
8753 #endif\r
8754 }\r
8755 \r
8756 VOID\r
8757 SetGNUMode()\r
8758 {\r
8759   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8760 }\r
8761 \r
8762 VOID\r
8763 SetNCPMode()\r
8764 {\r
8765   HMENU hmenu = GetMenu(hwndMain);\r
8766   SetMenuEnables(hmenu, ncpEnables);\r
8767   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8768     MF_BYPOSITION|MF_GRAYED);\r
8769     DrawMenuBar(hwndMain);\r
8770 }\r
8771 \r
8772 VOID\r
8773 SetCmailMode()\r
8774 {\r
8775   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8776 }\r
8777 \r
8778 VOID \r
8779 SetTrainingModeOn()\r
8780 {\r
8781   int i;\r
8782   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8783   for (i = 0; i < N_BUTTONS; i++) {\r
8784     if (buttonDesc[i].hwnd != NULL)\r
8785       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8786   }\r
8787   CommentPopDown();\r
8788 }\r
8789 \r
8790 VOID SetTrainingModeOff()\r
8791 {\r
8792   int i;\r
8793   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8794   for (i = 0; i < N_BUTTONS; i++) {\r
8795     if (buttonDesc[i].hwnd != NULL)\r
8796       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8797   }\r
8798 }\r
8799 \r
8800 \r
8801 VOID\r
8802 SetUserThinkingEnables()\r
8803 {\r
8804   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8805 }\r
8806 \r
8807 VOID\r
8808 SetMachineThinkingEnables()\r
8809 {\r
8810   HMENU hMenu = GetMenu(hwndMain);\r
8811   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8812 \r
8813   SetMenuEnables(hMenu, machineThinkingEnables);\r
8814 \r
8815   if (gameMode == MachinePlaysBlack) {\r
8816     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8817   } else if (gameMode == MachinePlaysWhite) {\r
8818     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8819   } else if (gameMode == TwoMachinesPlay) {\r
8820     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8821   }\r
8822 }\r
8823 \r
8824 \r
8825 VOID\r
8826 DisplayTitle(char *str)\r
8827 {\r
8828   char title[MSG_SIZ], *host;\r
8829   if (str[0] != NULLCHAR) {\r
8830     strcpy(title, str);\r
8831   } else if (appData.icsActive) {\r
8832     if (appData.icsCommPort[0] != NULLCHAR)\r
8833       host = "ICS";\r
8834     else \r
8835       host = appData.icsHost;\r
8836     sprintf(title, "%s: %s", szTitle, host);\r
8837   } else if (appData.noChessProgram) {\r
8838     strcpy(title, szTitle);\r
8839   } else {\r
8840     strcpy(title, szTitle);\r
8841     strcat(title, ": ");\r
8842     strcat(title, first.tidy);\r
8843   }\r
8844   SetWindowText(hwndMain, title);\r
8845 }\r
8846 \r
8847 \r
8848 VOID\r
8849 DisplayMessage(char *str1, char *str2)\r
8850 {\r
8851   HDC hdc;\r
8852   HFONT oldFont;\r
8853   int remain = MESSAGE_TEXT_MAX - 1;\r
8854   int len;\r
8855 \r
8856   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8857   messageText[0] = NULLCHAR;\r
8858   if (*str1) {\r
8859     len = strlen(str1);\r
8860     if (len > remain) len = remain;\r
8861     strncpy(messageText, str1, len);\r
8862     messageText[len] = NULLCHAR;\r
8863     remain -= len;\r
8864   }\r
8865   if (*str2 && remain >= 2) {\r
8866     if (*str1) {\r
8867       strcat(messageText, "  ");\r
8868       remain -= 2;\r
8869     }\r
8870     len = strlen(str2);\r
8871     if (len > remain) len = remain;\r
8872     strncat(messageText, str2, len);\r
8873   }\r
8874   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8875 \r
8876   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8877   hdc = GetDC(hwndMain);\r
8878   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8879   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8880              &messageRect, messageText, strlen(messageText), NULL);\r
8881   (void) SelectObject(hdc, oldFont);\r
8882   (void) ReleaseDC(hwndMain, hdc);\r
8883 }\r
8884 \r
8885 VOID\r
8886 DisplayError(char *str, int error)\r
8887 {\r
8888   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8889   int len;\r
8890 \r
8891   if (error == 0) {\r
8892     strcpy(buf, str);\r
8893   } else {\r
8894     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8895                         NULL, error, LANG_NEUTRAL,\r
8896                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8897     if (len > 0) {\r
8898       sprintf(buf, "%s:\n%s", str, buf2);\r
8899     } else {\r
8900       ErrorMap *em = errmap;\r
8901       while (em->err != 0 && em->err != error) em++;\r
8902       if (em->err != 0) {\r
8903         sprintf(buf, "%s:\n%s", str, em->msg);\r
8904       } else {\r
8905         sprintf(buf, "%s:\nError code %d", str, error);\r
8906       }\r
8907     }\r
8908   }\r
8909   \r
8910   ErrorPopUp("Error", buf);\r
8911 }\r
8912 \r
8913 \r
8914 VOID\r
8915 DisplayMoveError(char *str)\r
8916 {\r
8917   fromX = fromY = -1;\r
8918   ClearHighlights();\r
8919   DrawPosition(FALSE, NULL);\r
8920   if (appData.popupMoveErrors) {\r
8921     ErrorPopUp("Error", str);\r
8922   } else {\r
8923     DisplayMessage(str, "");\r
8924     moveErrorMessageUp = TRUE;\r
8925   }\r
8926 }\r
8927 \r
8928 VOID\r
8929 DisplayFatalError(char *str, int error, int exitStatus)\r
8930 {\r
8931   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8932   int len;\r
8933   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8934 \r
8935   if (error != 0) {\r
8936     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8937                         NULL, error, LANG_NEUTRAL,\r
8938                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8939     if (len > 0) {\r
8940       sprintf(buf, "%s:\n%s", str, buf2);\r
8941     } else {\r
8942       ErrorMap *em = errmap;\r
8943       while (em->err != 0 && em->err != error) em++;\r
8944       if (em->err != 0) {\r
8945         sprintf(buf, "%s:\n%s", str, em->msg);\r
8946       } else {\r
8947         sprintf(buf, "%s:\nError code %d", str, error);\r
8948       }\r
8949     }\r
8950     str = buf;\r
8951   }\r
8952   if (appData.debugMode) {\r
8953     fprintf(debugFP, "%s: %s\n", label, str);\r
8954   }\r
8955   if (appData.popupExitMessage) {\r
8956     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8957                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8958   }\r
8959   ExitEvent(exitStatus);\r
8960 }\r
8961 \r
8962 \r
8963 VOID\r
8964 DisplayInformation(char *str)\r
8965 {\r
8966   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8967 }\r
8968 \r
8969 \r
8970 VOID\r
8971 DisplayNote(char *str)\r
8972 {\r
8973   ErrorPopUp("Note", str);\r
8974 }\r
8975 \r
8976 \r
8977 typedef struct {\r
8978   char *title, *question, *replyPrefix;\r
8979   ProcRef pr;\r
8980 } QuestionParams;\r
8981 \r
8982 LRESULT CALLBACK\r
8983 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8984 {\r
8985   static QuestionParams *qp;\r
8986   char reply[MSG_SIZ];\r
8987   int len, err;\r
8988 \r
8989   switch (message) {\r
8990   case WM_INITDIALOG:\r
8991     qp = (QuestionParams *) lParam;\r
8992     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8993     SetWindowText(hDlg, qp->title);\r
8994     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8995     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8996     return FALSE;\r
8997 \r
8998   case WM_COMMAND:\r
8999     switch (LOWORD(wParam)) {\r
9000     case IDOK:\r
9001       strcpy(reply, qp->replyPrefix);\r
9002       if (*reply) strcat(reply, " ");\r
9003       len = strlen(reply);\r
9004       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9005       strcat(reply, "\n");\r
9006       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9007       EndDialog(hDlg, TRUE);\r
9008       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9009       return TRUE;\r
9010     case IDCANCEL:\r
9011       EndDialog(hDlg, FALSE);\r
9012       return TRUE;\r
9013     default:\r
9014       break;\r
9015     }\r
9016     break;\r
9017   }\r
9018   return FALSE;\r
9019 }\r
9020 \r
9021 VOID\r
9022 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9023 {\r
9024     QuestionParams qp;\r
9025     FARPROC lpProc;\r
9026     \r
9027     qp.title = title;\r
9028     qp.question = question;\r
9029     qp.replyPrefix = replyPrefix;\r
9030     qp.pr = pr;\r
9031     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9032     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9033       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9034     FreeProcInstance(lpProc);\r
9035 }\r
9036 \r
9037 /* [AS] Pick FRC position */\r
9038 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9039 {\r
9040     static int * lpIndexFRC;\r
9041     BOOL index_is_ok;\r
9042     char buf[16];\r
9043 \r
9044     switch( message )\r
9045     {\r
9046     case WM_INITDIALOG:\r
9047         lpIndexFRC = (int *) lParam;\r
9048 \r
9049         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9050 \r
9051         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9052         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9053         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9054         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9055 \r
9056         break;\r
9057 \r
9058     case WM_COMMAND:\r
9059         switch( LOWORD(wParam) ) {\r
9060         case IDOK:\r
9061             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9062             EndDialog( hDlg, 0 );\r
9063             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9064             return TRUE;\r
9065         case IDCANCEL:\r
9066             EndDialog( hDlg, 1 );   \r
9067             return TRUE;\r
9068         case IDC_NFG_Edit:\r
9069             if( HIWORD(wParam) == EN_CHANGE ) {\r
9070                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9071 \r
9072                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9073             }\r
9074             return TRUE;\r
9075         case IDC_NFG_Random:\r
9076             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9077             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9078             return TRUE;\r
9079         }\r
9080 \r
9081         break;\r
9082     }\r
9083 \r
9084     return FALSE;\r
9085 }\r
9086 \r
9087 int NewGameFRC()\r
9088 {\r
9089     int result;\r
9090     int index = appData.defaultFrcPosition;\r
9091     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9092 \r
9093     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9094 \r
9095     if( result == 0 ) {\r
9096         appData.defaultFrcPosition = index;\r
9097     }\r
9098 \r
9099     return result;\r
9100 }\r
9101 \r
9102 /* [AS] Game list options */\r
9103 typedef struct {\r
9104     char id;\r
9105     char * name;\r
9106 } GLT_Item;\r
9107 \r
9108 static GLT_Item GLT_ItemInfo[] = {\r
9109     { GLT_EVENT,      "Event" },\r
9110     { GLT_SITE,       "Site" },\r
9111     { GLT_DATE,       "Date" },\r
9112     { GLT_ROUND,      "Round" },\r
9113     { GLT_PLAYERS,    "Players" },\r
9114     { GLT_RESULT,     "Result" },\r
9115     { GLT_WHITE_ELO,  "White Rating" },\r
9116     { GLT_BLACK_ELO,  "Black Rating" },\r
9117     { GLT_TIME_CONTROL,"Time Control" },\r
9118     { GLT_VARIANT,    "Variant" },\r
9119     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9120     { 0, 0 }\r
9121 };\r
9122 \r
9123 const char * GLT_FindItem( char id )\r
9124 {\r
9125     const char * result = 0;\r
9126 \r
9127     GLT_Item * list = GLT_ItemInfo;\r
9128 \r
9129     while( list->id != 0 ) {\r
9130         if( list->id == id ) {\r
9131             result = list->name;\r
9132             break;\r
9133         }\r
9134 \r
9135         list++;\r
9136     }\r
9137 \r
9138     return result;\r
9139 }\r
9140 \r
9141 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9142 {\r
9143     const char * name = GLT_FindItem( id );\r
9144 \r
9145     if( name != 0 ) {\r
9146         if( index >= 0 ) {\r
9147             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9148         }\r
9149         else {\r
9150             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9151         }\r
9152     }\r
9153 }\r
9154 \r
9155 void GLT_TagsToList( HWND hDlg, char * tags )\r
9156 {\r
9157     char * pc = tags;\r
9158 \r
9159     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9160 \r
9161     while( *pc ) {\r
9162         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9163         pc++;\r
9164     }\r
9165 \r
9166     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9167 \r
9168     pc = GLT_ALL_TAGS;\r
9169 \r
9170     while( *pc ) {\r
9171         if( strchr( tags, *pc ) == 0 ) {\r
9172             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9173         }\r
9174         pc++;\r
9175     }\r
9176 \r
9177     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9178 }\r
9179 \r
9180 char GLT_ListItemToTag( HWND hDlg, int index )\r
9181 {\r
9182     char result = '\0';\r
9183     char name[128];\r
9184 \r
9185     GLT_Item * list = GLT_ItemInfo;\r
9186 \r
9187     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9188         while( list->id != 0 ) {\r
9189             if( strcmp( list->name, name ) == 0 ) {\r
9190                 result = list->id;\r
9191                 break;\r
9192             }\r
9193 \r
9194             list++;\r
9195         }\r
9196     }\r
9197 \r
9198     return result;\r
9199 }\r
9200 \r
9201 void GLT_MoveSelection( HWND hDlg, int delta )\r
9202 {\r
9203     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9204     int idx2 = idx1 + delta;\r
9205     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9206 \r
9207     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9208         char buf[128];\r
9209 \r
9210         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9211         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9212         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9213         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9214     }\r
9215 }\r
9216 \r
9217 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9218 {\r
9219     static char glt[64];\r
9220     static char * lpUserGLT;\r
9221 \r
9222     switch( message )\r
9223     {\r
9224     case WM_INITDIALOG:\r
9225         lpUserGLT = (char *) lParam;\r
9226         \r
9227         strcpy( glt, lpUserGLT );\r
9228 \r
9229         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9230 \r
9231         /* Initialize list */\r
9232         GLT_TagsToList( hDlg, glt );\r
9233 \r
9234         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9235 \r
9236         break;\r
9237 \r
9238     case WM_COMMAND:\r
9239         switch( LOWORD(wParam) ) {\r
9240         case IDOK:\r
9241             {\r
9242                 char * pc = lpUserGLT;\r
9243                 int idx = 0;\r
9244 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9245                 char id;\r
9246 \r
9247                 do {\r
9248                     id = GLT_ListItemToTag( hDlg, idx );\r
9249 \r
9250                     *pc++ = id;\r
9251                     idx++;\r
9252                 } while( id != '\0' );\r
9253             }\r
9254             EndDialog( hDlg, 0 );\r
9255             return TRUE;\r
9256         case IDCANCEL:\r
9257             EndDialog( hDlg, 1 );\r
9258             return TRUE;\r
9259 \r
9260         case IDC_GLT_Default:\r
9261             strcpy( glt, GLT_DEFAULT_TAGS );\r
9262             GLT_TagsToList( hDlg, glt );\r
9263             return TRUE;\r
9264 \r
9265         case IDC_GLT_Restore:\r
9266             strcpy( glt, lpUserGLT );\r
9267             GLT_TagsToList( hDlg, glt );\r
9268             return TRUE;\r
9269 \r
9270         case IDC_GLT_Up:\r
9271             GLT_MoveSelection( hDlg, -1 );\r
9272             return TRUE;\r
9273 \r
9274         case IDC_GLT_Down:\r
9275             GLT_MoveSelection( hDlg, +1 );\r
9276             return TRUE;\r
9277         }\r
9278 \r
9279         break;\r
9280     }\r
9281 \r
9282     return FALSE;\r
9283 }\r
9284 \r
9285 int GameListOptions()\r
9286 {\r
9287     char glt[64];\r
9288     int result;\r
9289     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9290 \r
9291     strcpy( glt, appData.gameListTags );\r
9292 \r
9293     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9294 \r
9295     if( result == 0 ) {\r
9296         /* [AS] Memory leak here! */\r
9297         appData.gameListTags = strdup( glt ); \r
9298     }\r
9299 \r
9300     return result;\r
9301 }\r
9302 \r
9303 \r
9304 VOID\r
9305 DisplayIcsInteractionTitle(char *str)\r
9306 {\r
9307   char consoleTitle[MSG_SIZ];\r
9308 \r
9309   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9310   SetWindowText(hwndConsole, consoleTitle);\r
9311 }\r
9312 \r
9313 void\r
9314 DrawPosition(int fullRedraw, Board board)\r
9315 {\r
9316   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9317 }\r
9318 \r
9319 \r
9320 VOID\r
9321 ResetFrontEnd()\r
9322 {\r
9323   fromX = fromY = -1;\r
9324   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9325     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9326     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9327     dragInfo.lastpos = dragInfo.pos;\r
9328     dragInfo.start.x = dragInfo.start.y = -1;\r
9329     dragInfo.from = dragInfo.start;\r
9330     ReleaseCapture();\r
9331     DrawPosition(TRUE, NULL);\r
9332   }\r
9333 }\r
9334 \r
9335 \r
9336 VOID\r
9337 CommentPopUp(char *title, char *str)\r
9338 {\r
9339   HWND hwnd = GetActiveWindow();\r
9340   EitherCommentPopUp(0, title, str, FALSE);\r
9341   SetActiveWindow(hwnd);\r
9342 }\r
9343 \r
9344 VOID\r
9345 CommentPopDown(void)\r
9346 {\r
9347   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9348   if (commentDialog) {\r
9349     ShowWindow(commentDialog, SW_HIDE);\r
9350   }\r
9351   commentDialogUp = FALSE;\r
9352 }\r
9353 \r
9354 VOID\r
9355 EditCommentPopUp(int index, char *title, char *str)\r
9356 {\r
9357   EitherCommentPopUp(index, title, str, TRUE);\r
9358 }\r
9359 \r
9360 \r
9361 VOID\r
9362 RingBell()\r
9363 {\r
9364   MyPlaySound(&sounds[(int)SoundMove]);\r
9365 }\r
9366 \r
9367 VOID PlayIcsWinSound()\r
9368 {\r
9369   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9370 }\r
9371 \r
9372 VOID PlayIcsLossSound()\r
9373 {\r
9374   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9375 }\r
9376 \r
9377 VOID PlayIcsDrawSound()\r
9378 {\r
9379   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9380 }\r
9381 \r
9382 VOID PlayIcsUnfinishedSound()\r
9383 {\r
9384   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9385 }\r
9386 \r
9387 VOID\r
9388 PlayAlarmSound()\r
9389 {\r
9390   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9391 }\r
9392 \r
9393 \r
9394 VOID\r
9395 EchoOn()\r
9396 {\r
9397   HWND hInput;\r
9398   consoleEcho = TRUE;\r
9399   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9400   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9401   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9402 }\r
9403 \r
9404 \r
9405 VOID\r
9406 EchoOff()\r
9407 {\r
9408   CHARFORMAT cf;\r
9409   HWND hInput;\r
9410   consoleEcho = FALSE;\r
9411   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9412   /* This works OK: set text and background both to the same color */\r
9413   cf = consoleCF;\r
9414   cf.crTextColor = COLOR_ECHOOFF;\r
9415   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9416   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9417 }\r
9418 \r
9419 /* No Raw()...? */\r
9420 \r
9421 void Colorize(ColorClass cc, int continuation)\r
9422 {\r
9423   currentColorClass = cc;\r
9424   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9425   consoleCF.crTextColor = textAttribs[cc].color;\r
9426   consoleCF.dwEffects = textAttribs[cc].effects;\r
9427   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9428 }\r
9429 \r
9430 char *\r
9431 UserName()\r
9432 {\r
9433   static char buf[MSG_SIZ];\r
9434   DWORD bufsiz = MSG_SIZ;\r
9435 \r
9436   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9437         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9438   }\r
9439   if (!GetUserName(buf, &bufsiz)) {\r
9440     /*DisplayError("Error getting user name", GetLastError());*/\r
9441     strcpy(buf, "User");\r
9442   }\r
9443   return buf;\r
9444 }\r
9445 \r
9446 char *\r
9447 HostName()\r
9448 {\r
9449   static char buf[MSG_SIZ];\r
9450   DWORD bufsiz = MSG_SIZ;\r
9451 \r
9452   if (!GetComputerName(buf, &bufsiz)) {\r
9453     /*DisplayError("Error getting host name", GetLastError());*/\r
9454     strcpy(buf, "Unknown");\r
9455   }\r
9456   return buf;\r
9457 }\r
9458 \r
9459 \r
9460 int\r
9461 ClockTimerRunning()\r
9462 {\r
9463   return clockTimerEvent != 0;\r
9464 }\r
9465 \r
9466 int\r
9467 StopClockTimer()\r
9468 {\r
9469   if (clockTimerEvent == 0) return FALSE;\r
9470   KillTimer(hwndMain, clockTimerEvent);\r
9471   clockTimerEvent = 0;\r
9472   return TRUE;\r
9473 }\r
9474 \r
9475 void\r
9476 StartClockTimer(long millisec)\r
9477 {\r
9478   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9479                              (UINT) millisec, NULL);\r
9480 }\r
9481 \r
9482 void\r
9483 DisplayWhiteClock(long timeRemaining, int highlight)\r
9484 {\r
9485   HDC hdc;\r
9486   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9487 \r
9488   if(appData.noGUI) return;\r
9489   hdc = GetDC(hwndMain);\r
9490   if (!IsIconic(hwndMain)) {\r
9491     DisplayAClock(hdc, timeRemaining, highlight, \r
9492                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9493   }\r
9494   if (highlight && iconCurrent == iconBlack) {\r
9495     iconCurrent = iconWhite;\r
9496     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9497     if (IsIconic(hwndMain)) {\r
9498       DrawIcon(hdc, 2, 2, iconCurrent);\r
9499     }\r
9500   }\r
9501   (void) ReleaseDC(hwndMain, hdc);\r
9502   if (hwndConsole)\r
9503     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9504 }\r
9505 \r
9506 void\r
9507 DisplayBlackClock(long timeRemaining, int highlight)\r
9508 {\r
9509   HDC hdc;\r
9510   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9511 \r
9512   if(appData.noGUI) return;\r
9513   hdc = GetDC(hwndMain);\r
9514   if (!IsIconic(hwndMain)) {\r
9515     DisplayAClock(hdc, timeRemaining, highlight, \r
9516                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9517   }\r
9518   if (highlight && iconCurrent == iconWhite) {\r
9519     iconCurrent = iconBlack;\r
9520     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9521     if (IsIconic(hwndMain)) {\r
9522       DrawIcon(hdc, 2, 2, iconCurrent);\r
9523     }\r
9524   }\r
9525   (void) ReleaseDC(hwndMain, hdc);\r
9526   if (hwndConsole)\r
9527     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9528 }\r
9529 \r
9530 \r
9531 int\r
9532 LoadGameTimerRunning()\r
9533 {\r
9534   return loadGameTimerEvent != 0;\r
9535 }\r
9536 \r
9537 int\r
9538 StopLoadGameTimer()\r
9539 {\r
9540   if (loadGameTimerEvent == 0) return FALSE;\r
9541   KillTimer(hwndMain, loadGameTimerEvent);\r
9542   loadGameTimerEvent = 0;\r
9543   return TRUE;\r
9544 }\r
9545 \r
9546 void\r
9547 StartLoadGameTimer(long millisec)\r
9548 {\r
9549   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9550                                 (UINT) millisec, NULL);\r
9551 }\r
9552 \r
9553 void\r
9554 AutoSaveGame()\r
9555 {\r
9556   char *defName;\r
9557   FILE *f;\r
9558   char fileTitle[MSG_SIZ];\r
9559 \r
9560   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9561   f = OpenFileDialog(hwndMain, "a", defName,\r
9562                      appData.oldSaveStyle ? "gam" : "pgn",\r
9563                      GAME_FILT, \r
9564                      "Save Game to File", NULL, fileTitle, NULL);\r
9565   if (f != NULL) {\r
9566     SaveGame(f, 0, "");\r
9567     fclose(f);\r
9568   }\r
9569 }\r
9570 \r
9571 \r
9572 void\r
9573 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9574 {\r
9575   if (delayedTimerEvent != 0) {\r
9576     if (appData.debugMode) {\r
9577       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9578     }\r
9579     KillTimer(hwndMain, delayedTimerEvent);\r
9580     delayedTimerEvent = 0;\r
9581     delayedTimerCallback();\r
9582   }\r
9583   delayedTimerCallback = cb;\r
9584   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9585                                 (UINT) millisec, NULL);\r
9586 }\r
9587 \r
9588 DelayedEventCallback\r
9589 GetDelayedEvent()\r
9590 {\r
9591   if (delayedTimerEvent) {\r
9592     return delayedTimerCallback;\r
9593   } else {\r
9594     return NULL;\r
9595   }\r
9596 }\r
9597 \r
9598 void\r
9599 CancelDelayedEvent()\r
9600 {\r
9601   if (delayedTimerEvent) {\r
9602     KillTimer(hwndMain, delayedTimerEvent);\r
9603     delayedTimerEvent = 0;\r
9604   }\r
9605 }\r
9606 \r
9607 DWORD GetWin32Priority(int nice)\r
9608 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9609 /*\r
9610 REALTIME_PRIORITY_CLASS     0x00000100\r
9611 HIGH_PRIORITY_CLASS         0x00000080\r
9612 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9613 NORMAL_PRIORITY_CLASS       0x00000020\r
9614 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9615 IDLE_PRIORITY_CLASS         0x00000040\r
9616 */\r
9617         if (nice < -15) return 0x00000080;\r
9618         if (nice < 0)   return 0x00008000;\r
9619         if (nice == 0)  return 0x00000020;\r
9620         if (nice < 15)  return 0x00004000;\r
9621         return 0x00000040;\r
9622 }\r
9623 \r
9624 /* Start a child process running the given program.\r
9625    The process's standard output can be read from "from", and its\r
9626    standard input can be written to "to".\r
9627    Exit with fatal error if anything goes wrong.\r
9628    Returns an opaque pointer that can be used to destroy the process\r
9629    later.\r
9630 */\r
9631 int\r
9632 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9633 {\r
9634 #define BUFSIZE 4096\r
9635 \r
9636   HANDLE hChildStdinRd, hChildStdinWr,\r
9637     hChildStdoutRd, hChildStdoutWr;\r
9638   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9639   SECURITY_ATTRIBUTES saAttr;\r
9640   BOOL fSuccess;\r
9641   PROCESS_INFORMATION piProcInfo;\r
9642   STARTUPINFO siStartInfo;\r
9643   ChildProc *cp;\r
9644   char buf[MSG_SIZ];\r
9645   DWORD err;\r
9646 \r
9647   if (appData.debugMode) {\r
9648     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9649   }\r
9650 \r
9651   *pr = NoProc;\r
9652 \r
9653   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9654   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9655   saAttr.bInheritHandle = TRUE;\r
9656   saAttr.lpSecurityDescriptor = NULL;\r
9657 \r
9658   /*\r
9659    * The steps for redirecting child's STDOUT:\r
9660    *     1. Create anonymous pipe to be STDOUT for child.\r
9661    *     2. Create a noninheritable duplicate of read handle,\r
9662    *         and close the inheritable read handle.\r
9663    */\r
9664 \r
9665   /* Create a pipe for the child's STDOUT. */\r
9666   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9667     return GetLastError();\r
9668   }\r
9669 \r
9670   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9671   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9672                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9673                              FALSE,     /* not inherited */\r
9674                              DUPLICATE_SAME_ACCESS);\r
9675   if (! fSuccess) {\r
9676     return GetLastError();\r
9677   }\r
9678   CloseHandle(hChildStdoutRd);\r
9679 \r
9680   /*\r
9681    * The steps for redirecting child's STDIN:\r
9682    *     1. Create anonymous pipe to be STDIN for child.\r
9683    *     2. Create a noninheritable duplicate of write handle,\r
9684    *         and close the inheritable write handle.\r
9685    */\r
9686 \r
9687   /* Create a pipe for the child's STDIN. */\r
9688   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9689     return GetLastError();\r
9690   }\r
9691 \r
9692   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9693   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9694                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9695                              FALSE,     /* not inherited */\r
9696                              DUPLICATE_SAME_ACCESS);\r
9697   if (! fSuccess) {\r
9698     return GetLastError();\r
9699   }\r
9700   CloseHandle(hChildStdinWr);\r
9701 \r
9702   /* Arrange to (1) look in dir for the child .exe file, and\r
9703    * (2) have dir be the child's working directory.  Interpret\r
9704    * dir relative to the directory WinBoard loaded from. */\r
9705   GetCurrentDirectory(MSG_SIZ, buf);\r
9706   SetCurrentDirectory(installDir);\r
9707   SetCurrentDirectory(dir);\r
9708 \r
9709   /* Now create the child process. */\r
9710 \r
9711   siStartInfo.cb = sizeof(STARTUPINFO);\r
9712   siStartInfo.lpReserved = NULL;\r
9713   siStartInfo.lpDesktop = NULL;\r
9714   siStartInfo.lpTitle = NULL;\r
9715   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9716   siStartInfo.cbReserved2 = 0;\r
9717   siStartInfo.lpReserved2 = NULL;\r
9718   siStartInfo.hStdInput = hChildStdinRd;\r
9719   siStartInfo.hStdOutput = hChildStdoutWr;\r
9720   siStartInfo.hStdError = hChildStdoutWr;\r
9721 \r
9722   fSuccess = CreateProcess(NULL,\r
9723                            cmdLine,        /* command line */\r
9724                            NULL,           /* process security attributes */\r
9725                            NULL,           /* primary thread security attrs */\r
9726                            TRUE,           /* handles are inherited */\r
9727                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9728                            NULL,           /* use parent's environment */\r
9729                            NULL,\r
9730                            &siStartInfo, /* STARTUPINFO pointer */\r
9731                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9732 \r
9733   err = GetLastError();\r
9734   SetCurrentDirectory(buf); /* return to prev directory */\r
9735   if (! fSuccess) {\r
9736     return err;\r
9737   }\r
9738 \r
9739   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9740     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9741     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9742   }\r
9743 \r
9744   /* Close the handles we don't need in the parent */\r
9745   CloseHandle(piProcInfo.hThread);\r
9746   CloseHandle(hChildStdinRd);\r
9747   CloseHandle(hChildStdoutWr);\r
9748 \r
9749   /* Prepare return value */\r
9750   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9751   cp->kind = CPReal;\r
9752   cp->hProcess = piProcInfo.hProcess;\r
9753   cp->pid = piProcInfo.dwProcessId;\r
9754   cp->hFrom = hChildStdoutRdDup;\r
9755   cp->hTo = hChildStdinWrDup;\r
9756 \r
9757   *pr = (void *) cp;\r
9758 \r
9759   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9760      2000 where engines sometimes don't see the initial command(s)\r
9761      from WinBoard and hang.  I don't understand how that can happen,\r
9762      but the Sleep is harmless, so I've put it in.  Others have also\r
9763      reported what may be the same problem, so hopefully this will fix\r
9764      it for them too.  */\r
9765   Sleep(500);\r
9766 \r
9767   return NO_ERROR;\r
9768 }\r
9769 \r
9770 \r
9771 void\r
9772 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9773 {\r
9774   ChildProc *cp; int result;\r
9775 \r
9776   cp = (ChildProc *) pr;\r
9777   if (cp == NULL) return;\r
9778 \r
9779   switch (cp->kind) {\r
9780   case CPReal:\r
9781     /* TerminateProcess is considered harmful, so... */\r
9782     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9783     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9784     /* The following doesn't work because the chess program\r
9785        doesn't "have the same console" as WinBoard.  Maybe\r
9786        we could arrange for this even though neither WinBoard\r
9787        nor the chess program uses a console for stdio? */\r
9788     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9789 \r
9790     /* [AS] Special termination modes for misbehaving programs... */\r
9791     if( signal == 9 ) { \r
9792         result = TerminateProcess( cp->hProcess, 0 );\r
9793 \r
9794         if ( appData.debugMode) {\r
9795             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9796         }\r
9797     }\r
9798     else if( signal == 10 ) {\r
9799         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9800 \r
9801         if( dw != WAIT_OBJECT_0 ) {\r
9802             result = TerminateProcess( cp->hProcess, 0 );\r
9803 \r
9804             if ( appData.debugMode) {\r
9805                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9806             }\r
9807 \r
9808         }\r
9809     }\r
9810 \r
9811     CloseHandle(cp->hProcess);\r
9812     break;\r
9813 \r
9814   case CPComm:\r
9815     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9816     break;\r
9817 \r
9818   case CPSock:\r
9819     closesocket(cp->sock);\r
9820     WSACleanup();\r
9821     break;\r
9822 \r
9823   case CPRcmd:\r
9824     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9825     closesocket(cp->sock);\r
9826     closesocket(cp->sock2);\r
9827     WSACleanup();\r
9828     break;\r
9829   }\r
9830   free(cp);\r
9831 }\r
9832 \r
9833 void\r
9834 InterruptChildProcess(ProcRef pr)\r
9835 {\r
9836   ChildProc *cp;\r
9837 \r
9838   cp = (ChildProc *) pr;\r
9839   if (cp == NULL) return;\r
9840   switch (cp->kind) {\r
9841   case CPReal:\r
9842     /* The following doesn't work because the chess program\r
9843        doesn't "have the same console" as WinBoard.  Maybe\r
9844        we could arrange for this even though neither WinBoard\r
9845        nor the chess program uses a console for stdio */\r
9846     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9847     break;\r
9848 \r
9849   case CPComm:\r
9850   case CPSock:\r
9851     /* Can't interrupt */\r
9852     break;\r
9853 \r
9854   case CPRcmd:\r
9855     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9856     break;\r
9857   }\r
9858 }\r
9859 \r
9860 \r
9861 int\r
9862 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9863 {\r
9864   char cmdLine[MSG_SIZ];\r
9865 \r
9866   if (port[0] == NULLCHAR) {\r
9867     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9868   } else {\r
9869     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9870   }\r
9871   return StartChildProcess(cmdLine, "", pr);\r
9872 }\r
9873 \r
9874 \r
9875 /* Code to open TCP sockets */\r
9876 \r
9877 int\r
9878 OpenTCP(char *host, char *port, ProcRef *pr)\r
9879 {\r
9880   ChildProc *cp;\r
9881   int err;\r
9882   SOCKET s;\r
9883   struct sockaddr_in sa, mysa;\r
9884   struct hostent FAR *hp;\r
9885   unsigned short uport;\r
9886   WORD wVersionRequested;\r
9887   WSADATA wsaData;\r
9888 \r
9889   /* Initialize socket DLL */\r
9890   wVersionRequested = MAKEWORD(1, 1);\r
9891   err = WSAStartup(wVersionRequested, &wsaData);\r
9892   if (err != 0) return err;\r
9893 \r
9894   /* Make socket */\r
9895   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9896     err = WSAGetLastError();\r
9897     WSACleanup();\r
9898     return err;\r
9899   }\r
9900 \r
9901   /* Bind local address using (mostly) don't-care values.\r
9902    */\r
9903   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9904   mysa.sin_family = AF_INET;\r
9905   mysa.sin_addr.s_addr = INADDR_ANY;\r
9906   uport = (unsigned short) 0;\r
9907   mysa.sin_port = htons(uport);\r
9908   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9909       == SOCKET_ERROR) {\r
9910     err = WSAGetLastError();\r
9911     WSACleanup();\r
9912     return err;\r
9913   }\r
9914 \r
9915   /* Resolve remote host name */\r
9916   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9917   if (!(hp = gethostbyname(host))) {\r
9918     unsigned int b0, b1, b2, b3;\r
9919 \r
9920     err = WSAGetLastError();\r
9921 \r
9922     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9923       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9924       hp->h_addrtype = AF_INET;\r
9925       hp->h_length = 4;\r
9926       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9927       hp->h_addr_list[0] = (char *) malloc(4);\r
9928       hp->h_addr_list[0][0] = (char) b0;\r
9929       hp->h_addr_list[0][1] = (char) b1;\r
9930       hp->h_addr_list[0][2] = (char) b2;\r
9931       hp->h_addr_list[0][3] = (char) b3;\r
9932     } else {\r
9933       WSACleanup();\r
9934       return err;\r
9935     }\r
9936   }\r
9937   sa.sin_family = hp->h_addrtype;\r
9938   uport = (unsigned short) atoi(port);\r
9939   sa.sin_port = htons(uport);\r
9940   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9941 \r
9942   /* Make connection */\r
9943   if (connect(s, (struct sockaddr *) &sa,\r
9944               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9945     err = WSAGetLastError();\r
9946     WSACleanup();\r
9947     return err;\r
9948   }\r
9949 \r
9950   /* Prepare return value */\r
9951   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9952   cp->kind = CPSock;\r
9953   cp->sock = s;\r
9954   *pr = (ProcRef *) cp;\r
9955 \r
9956   return NO_ERROR;\r
9957 }\r
9958 \r
9959 int\r
9960 OpenCommPort(char *name, ProcRef *pr)\r
9961 {\r
9962   HANDLE h;\r
9963   COMMTIMEOUTS ct;\r
9964   ChildProc *cp;\r
9965   char fullname[MSG_SIZ];\r
9966 \r
9967   if (*name != '\\')\r
9968     sprintf(fullname, "\\\\.\\%s", name);\r
9969   else\r
9970     strcpy(fullname, name);\r
9971 \r
9972   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9973                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9974   if (h == (HANDLE) -1) {\r
9975     return GetLastError();\r
9976   }\r
9977   hCommPort = h;\r
9978 \r
9979   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9980 \r
9981   /* Accumulate characters until a 100ms pause, then parse */\r
9982   ct.ReadIntervalTimeout = 100;\r
9983   ct.ReadTotalTimeoutMultiplier = 0;\r
9984   ct.ReadTotalTimeoutConstant = 0;\r
9985   ct.WriteTotalTimeoutMultiplier = 0;\r
9986   ct.WriteTotalTimeoutConstant = 0;\r
9987   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9988 \r
9989   /* Prepare return value */\r
9990   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9991   cp->kind = CPComm;\r
9992   cp->hFrom = h;\r
9993   cp->hTo = h;\r
9994   *pr = (ProcRef *) cp;\r
9995 \r
9996   return NO_ERROR;\r
9997 }\r
9998 \r
9999 int\r
10000 OpenLoopback(ProcRef *pr)\r
10001 {\r
10002   DisplayFatalError("Not implemented", 0, 1);\r
10003   return NO_ERROR;\r
10004 }\r
10005 \r
10006 \r
10007 int\r
10008 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10009 {\r
10010   ChildProc *cp;\r
10011   int err;\r
10012   SOCKET s, s2, s3;\r
10013   struct sockaddr_in sa, mysa;\r
10014   struct hostent FAR *hp;\r
10015   unsigned short uport;\r
10016   WORD wVersionRequested;\r
10017   WSADATA wsaData;\r
10018   int fromPort;\r
10019   char stderrPortStr[MSG_SIZ];\r
10020 \r
10021   /* Initialize socket DLL */\r
10022   wVersionRequested = MAKEWORD(1, 1);\r
10023   err = WSAStartup(wVersionRequested, &wsaData);\r
10024   if (err != 0) return err;\r
10025 \r
10026   /* Resolve remote host name */\r
10027   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10028   if (!(hp = gethostbyname(host))) {\r
10029     unsigned int b0, b1, b2, b3;\r
10030 \r
10031     err = WSAGetLastError();\r
10032 \r
10033     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10034       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10035       hp->h_addrtype = AF_INET;\r
10036       hp->h_length = 4;\r
10037       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10038       hp->h_addr_list[0] = (char *) malloc(4);\r
10039       hp->h_addr_list[0][0] = (char) b0;\r
10040       hp->h_addr_list[0][1] = (char) b1;\r
10041       hp->h_addr_list[0][2] = (char) b2;\r
10042       hp->h_addr_list[0][3] = (char) b3;\r
10043     } else {\r
10044       WSACleanup();\r
10045       return err;\r
10046     }\r
10047   }\r
10048   sa.sin_family = hp->h_addrtype;\r
10049   uport = (unsigned short) 514;\r
10050   sa.sin_port = htons(uport);\r
10051   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10052 \r
10053   /* Bind local socket to unused "privileged" port address\r
10054    */\r
10055   s = INVALID_SOCKET;\r
10056   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10057   mysa.sin_family = AF_INET;\r
10058   mysa.sin_addr.s_addr = INADDR_ANY;\r
10059   for (fromPort = 1023;; fromPort--) {\r
10060     if (fromPort < 0) {\r
10061       WSACleanup();\r
10062       return WSAEADDRINUSE;\r
10063     }\r
10064     if (s == INVALID_SOCKET) {\r
10065       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10066         err = WSAGetLastError();\r
10067         WSACleanup();\r
10068         return err;\r
10069       }\r
10070     }\r
10071     uport = (unsigned short) fromPort;\r
10072     mysa.sin_port = htons(uport);\r
10073     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10074         == SOCKET_ERROR) {\r
10075       err = WSAGetLastError();\r
10076       if (err == WSAEADDRINUSE) continue;\r
10077       WSACleanup();\r
10078       return err;\r
10079     }\r
10080     if (connect(s, (struct sockaddr *) &sa,\r
10081       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10082       err = WSAGetLastError();\r
10083       if (err == WSAEADDRINUSE) {\r
10084         closesocket(s);\r
10085         s = -1;\r
10086         continue;\r
10087       }\r
10088       WSACleanup();\r
10089       return err;\r
10090     }\r
10091     break;\r
10092   }\r
10093 \r
10094   /* Bind stderr local socket to unused "privileged" port address\r
10095    */\r
10096   s2 = INVALID_SOCKET;\r
10097   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10098   mysa.sin_family = AF_INET;\r
10099   mysa.sin_addr.s_addr = INADDR_ANY;\r
10100   for (fromPort = 1023;; fromPort--) {\r
10101     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10102     if (fromPort < 0) {\r
10103       (void) closesocket(s);\r
10104       WSACleanup();\r
10105       return WSAEADDRINUSE;\r
10106     }\r
10107     if (s2 == INVALID_SOCKET) {\r
10108       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10109         err = WSAGetLastError();\r
10110         closesocket(s);\r
10111         WSACleanup();\r
10112         return err;\r
10113       }\r
10114     }\r
10115     uport = (unsigned short) fromPort;\r
10116     mysa.sin_port = htons(uport);\r
10117     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10118         == SOCKET_ERROR) {\r
10119       err = WSAGetLastError();\r
10120       if (err == WSAEADDRINUSE) continue;\r
10121       (void) closesocket(s);\r
10122       WSACleanup();\r
10123       return err;\r
10124     }\r
10125     if (listen(s2, 1) == SOCKET_ERROR) {\r
10126       err = WSAGetLastError();\r
10127       if (err == WSAEADDRINUSE) {\r
10128         closesocket(s2);\r
10129         s2 = INVALID_SOCKET;\r
10130         continue;\r
10131       }\r
10132       (void) closesocket(s);\r
10133       (void) closesocket(s2);\r
10134       WSACleanup();\r
10135       return err;\r
10136     }\r
10137     break;\r
10138   }\r
10139   prevStderrPort = fromPort; // remember port used\r
10140   sprintf(stderrPortStr, "%d", fromPort);\r
10141 \r
10142   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10143     err = WSAGetLastError();\r
10144     (void) closesocket(s);\r
10145     (void) closesocket(s2);\r
10146     WSACleanup();\r
10147     return err;\r
10148   }\r
10149 \r
10150   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10151     err = WSAGetLastError();\r
10152     (void) closesocket(s);\r
10153     (void) closesocket(s2);\r
10154     WSACleanup();\r
10155     return err;\r
10156   }\r
10157   if (*user == NULLCHAR) user = UserName();\r
10158   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10159     err = WSAGetLastError();\r
10160     (void) closesocket(s);\r
10161     (void) closesocket(s2);\r
10162     WSACleanup();\r
10163     return err;\r
10164   }\r
10165   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10166     err = WSAGetLastError();\r
10167     (void) closesocket(s);\r
10168     (void) closesocket(s2);\r
10169     WSACleanup();\r
10170     return err;\r
10171   }\r
10172 \r
10173   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10174     err = WSAGetLastError();\r
10175     (void) closesocket(s);\r
10176     (void) closesocket(s2);\r
10177     WSACleanup();\r
10178     return err;\r
10179   }\r
10180   (void) closesocket(s2);  /* Stop listening */\r
10181 \r
10182   /* Prepare return value */\r
10183   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10184   cp->kind = CPRcmd;\r
10185   cp->sock = s;\r
10186   cp->sock2 = s3;\r
10187   *pr = (ProcRef *) cp;\r
10188 \r
10189   return NO_ERROR;\r
10190 }\r
10191 \r
10192 \r
10193 InputSourceRef\r
10194 AddInputSource(ProcRef pr, int lineByLine,\r
10195                InputCallback func, VOIDSTAR closure)\r
10196 {\r
10197   InputSource *is, *is2 = NULL;\r
10198   ChildProc *cp = (ChildProc *) pr;\r
10199 \r
10200   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10201   is->lineByLine = lineByLine;\r
10202   is->func = func;\r
10203   is->closure = closure;\r
10204   is->second = NULL;\r
10205   is->next = is->buf;\r
10206   if (pr == NoProc) {\r
10207     is->kind = CPReal;\r
10208     consoleInputSource = is;\r
10209   } else {\r
10210     is->kind = cp->kind;\r
10211     /* \r
10212         [AS] Try to avoid a race condition if the thread is given control too early:\r
10213         we create all threads suspended so that the is->hThread variable can be\r
10214         safely assigned, then let the threads start with ResumeThread.\r
10215     */\r
10216     switch (cp->kind) {\r
10217     case CPReal:\r
10218       is->hFile = cp->hFrom;\r
10219       cp->hFrom = NULL; /* now owned by InputThread */\r
10220       is->hThread =\r
10221         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10222                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10223       break;\r
10224 \r
10225     case CPComm:\r
10226       is->hFile = cp->hFrom;\r
10227       cp->hFrom = NULL; /* now owned by InputThread */\r
10228       is->hThread =\r
10229         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10230                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10231       break;\r
10232 \r
10233     case CPSock:\r
10234       is->sock = cp->sock;\r
10235       is->hThread =\r
10236         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10237                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10238       break;\r
10239 \r
10240     case CPRcmd:\r
10241       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10242       *is2 = *is;\r
10243       is->sock = cp->sock;\r
10244       is->second = is2;\r
10245       is2->sock = cp->sock2;\r
10246       is2->second = is2;\r
10247       is->hThread =\r
10248         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10249                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10250       is2->hThread =\r
10251         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10252                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10253       break;\r
10254     }\r
10255 \r
10256     if( is->hThread != NULL ) {\r
10257         ResumeThread( is->hThread );\r
10258     }\r
10259 \r
10260     if( is2 != NULL && is2->hThread != NULL ) {\r
10261         ResumeThread( is2->hThread );\r
10262     }\r
10263   }\r
10264 \r
10265   return (InputSourceRef) is;\r
10266 }\r
10267 \r
10268 void\r
10269 RemoveInputSource(InputSourceRef isr)\r
10270 {\r
10271   InputSource *is;\r
10272 \r
10273   is = (InputSource *) isr;\r
10274   is->hThread = NULL;  /* tell thread to stop */\r
10275   CloseHandle(is->hThread);\r
10276   if (is->second != NULL) {\r
10277     is->second->hThread = NULL;\r
10278     CloseHandle(is->second->hThread);\r
10279   }\r
10280 }\r
10281 \r
10282 \r
10283 int\r
10284 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10285 {\r
10286   DWORD dOutCount;\r
10287   int outCount = SOCKET_ERROR;\r
10288   ChildProc *cp = (ChildProc *) pr;\r
10289   static OVERLAPPED ovl;\r
10290 \r
10291   if (pr == NoProc) {\r
10292     ConsoleOutput(message, count, FALSE);\r
10293     return count;\r
10294   } \r
10295 \r
10296   if (ovl.hEvent == NULL) {\r
10297     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10298   }\r
10299   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10300 \r
10301   switch (cp->kind) {\r
10302   case CPSock:\r
10303   case CPRcmd:\r
10304     outCount = send(cp->sock, message, count, 0);\r
10305     if (outCount == SOCKET_ERROR) {\r
10306       *outError = WSAGetLastError();\r
10307     } else {\r
10308       *outError = NO_ERROR;\r
10309     }\r
10310     break;\r
10311 \r
10312   case CPReal:\r
10313     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10314                   &dOutCount, NULL)) {\r
10315       *outError = NO_ERROR;\r
10316       outCount = (int) dOutCount;\r
10317     } else {\r
10318       *outError = GetLastError();\r
10319     }\r
10320     break;\r
10321 \r
10322   case CPComm:\r
10323     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10324                             &dOutCount, &ovl);\r
10325     if (*outError == NO_ERROR) {\r
10326       outCount = (int) dOutCount;\r
10327     }\r
10328     break;\r
10329   }\r
10330   return outCount;\r
10331 }\r
10332 \r
10333 int\r
10334 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10335                        long msdelay)\r
10336 {\r
10337   /* Ignore delay, not implemented for WinBoard */\r
10338   return OutputToProcess(pr, message, count, outError);\r
10339 }\r
10340 \r
10341 \r
10342 void\r
10343 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10344                         char *buf, int count, int error)\r
10345 {\r
10346   DisplayFatalError("Not implemented", 0, 1);\r
10347 }\r
10348 \r
10349 /* see wgamelist.c for Game List functions */\r
10350 /* see wedittags.c for Edit Tags functions */\r
10351 \r
10352 \r
10353 VOID\r
10354 ICSInitScript()\r
10355 {\r
10356   FILE *f;\r
10357   char buf[MSG_SIZ];\r
10358   char *dummy;\r
10359 \r
10360   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10361     f = fopen(buf, "r");\r
10362     if (f != NULL) {\r
10363       ProcessICSInitScript(f);\r
10364       fclose(f);\r
10365     }\r
10366   }\r
10367 }\r
10368 \r
10369 \r
10370 VOID\r
10371 StartAnalysisClock()\r
10372 {\r
10373   if (analysisTimerEvent) return;\r
10374   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10375                                         (UINT) 2000, NULL);\r
10376 }\r
10377 \r
10378 LRESULT CALLBACK\r
10379 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10380 {\r
10381   static HANDLE hwndText;\r
10382   RECT rect;\r
10383   static int sizeX, sizeY;\r
10384   int newSizeX, newSizeY, flags;\r
10385   MINMAXINFO *mmi;\r
10386 \r
10387   switch (message) {\r
10388   case WM_INITDIALOG: /* message: initialize dialog box */\r
10389     /* Initialize the dialog items */\r
10390     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10391     SetWindowText(hDlg, analysisTitle);\r
10392     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10393     /* Size and position the dialog */\r
10394     if (!analysisDialog) {\r
10395       analysisDialog = hDlg;\r
10396       flags = SWP_NOZORDER;\r
10397       GetClientRect(hDlg, &rect);\r
10398       sizeX = rect.right;\r
10399       sizeY = rect.bottom;\r
10400       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10401           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10402         WINDOWPLACEMENT wp;\r
10403         EnsureOnScreen(&analysisX, &analysisY);\r
10404         wp.length = sizeof(WINDOWPLACEMENT);\r
10405         wp.flags = 0;\r
10406         wp.showCmd = SW_SHOW;\r
10407         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10408         wp.rcNormalPosition.left = analysisX;\r
10409         wp.rcNormalPosition.right = analysisX + analysisW;\r
10410         wp.rcNormalPosition.top = analysisY;\r
10411         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10412         SetWindowPlacement(hDlg, &wp);\r
10413 \r
10414         GetClientRect(hDlg, &rect);\r
10415         newSizeX = rect.right;\r
10416         newSizeY = rect.bottom;\r
10417         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10418                               newSizeX, newSizeY);\r
10419         sizeX = newSizeX;\r
10420         sizeY = newSizeY;\r
10421       }\r
10422     }\r
10423     return FALSE;\r
10424 \r
10425   case WM_COMMAND: /* message: received a command */\r
10426     switch (LOWORD(wParam)) {\r
10427     case IDCANCEL:\r
10428       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10429           ExitAnalyzeMode();\r
10430           ModeHighlight();\r
10431           return TRUE;\r
10432       }\r
10433       EditGameEvent();\r
10434       return TRUE;\r
10435     default:\r
10436       break;\r
10437     }\r
10438     break;\r
10439 \r
10440   case WM_SIZE:\r
10441     newSizeX = LOWORD(lParam);\r
10442     newSizeY = HIWORD(lParam);\r
10443     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10444     sizeX = newSizeX;\r
10445     sizeY = newSizeY;\r
10446     break;\r
10447 \r
10448   case WM_GETMINMAXINFO:\r
10449     /* Prevent resizing window too small */\r
10450     mmi = (MINMAXINFO *) lParam;\r
10451     mmi->ptMinTrackSize.x = 100;\r
10452     mmi->ptMinTrackSize.y = 100;\r
10453     break;\r
10454   }\r
10455   return FALSE;\r
10456 }\r
10457 \r
10458 VOID\r
10459 AnalysisPopUp(char* title, char* str)\r
10460 {\r
10461   FARPROC lpProc;\r
10462   char *p, *q;\r
10463 \r
10464   /* [AS] */\r
10465   EngineOutputPopUp();\r
10466   return;\r
10467 \r
10468   if (str == NULL) str = "";\r
10469   p = (char *) malloc(2 * strlen(str) + 2);\r
10470   q = p;\r
10471   while (*str) {\r
10472     if (*str == '\n') *q++ = '\r';\r
10473     *q++ = *str++;\r
10474   }\r
10475   *q = NULLCHAR;\r
10476   if (analysisText != NULL) free(analysisText);\r
10477   analysisText = p;\r
10478 \r
10479   if (analysisDialog) {\r
10480     SetWindowText(analysisDialog, title);\r
10481     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10482     ShowWindow(analysisDialog, SW_SHOW);\r
10483   } else {\r
10484     analysisTitle = title;\r
10485     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10486     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10487                  hwndMain, (DLGPROC)lpProc);\r
10488     FreeProcInstance(lpProc);\r
10489   }\r
10490   analysisDialogUp = TRUE;  \r
10491 }\r
10492 \r
10493 VOID\r
10494 AnalysisPopDown()\r
10495 {\r
10496   if (analysisDialog) {\r
10497     ShowWindow(analysisDialog, SW_HIDE);\r
10498   }\r
10499   analysisDialogUp = FALSE;  \r
10500 }\r
10501 \r
10502 \r
10503 VOID\r
10504 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10505 {\r
10506   highlightInfo.sq[0].x = fromX;\r
10507   highlightInfo.sq[0].y = fromY;\r
10508   highlightInfo.sq[1].x = toX;\r
10509   highlightInfo.sq[1].y = toY;\r
10510 }\r
10511 \r
10512 VOID\r
10513 ClearHighlights()\r
10514 {\r
10515   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10516     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10517 }\r
10518 \r
10519 VOID\r
10520 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10521 {\r
10522   premoveHighlightInfo.sq[0].x = fromX;\r
10523   premoveHighlightInfo.sq[0].y = fromY;\r
10524   premoveHighlightInfo.sq[1].x = toX;\r
10525   premoveHighlightInfo.sq[1].y = toY;\r
10526 }\r
10527 \r
10528 VOID\r
10529 ClearPremoveHighlights()\r
10530 {\r
10531   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10532     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10533 }\r
10534 \r
10535 VOID\r
10536 ShutDownFrontEnd()\r
10537 {\r
10538   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10539   DeleteClipboardTempFiles();\r
10540 }\r
10541 \r
10542 void\r
10543 BoardToTop()\r
10544 {\r
10545     if (IsIconic(hwndMain))\r
10546       ShowWindow(hwndMain, SW_RESTORE);\r
10547 \r
10548     SetActiveWindow(hwndMain);\r
10549 }\r
10550 \r
10551 /*\r
10552  * Prototypes for animation support routines\r
10553  */\r
10554 static void ScreenSquare(int column, int row, POINT * pt);\r
10555 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10556      POINT frames[], int * nFrames);\r
10557 \r
10558 \r
10559 #define kFactor 4\r
10560 \r
10561 void\r
10562 AnimateMove(board, fromX, fromY, toX, toY)\r
10563      Board board;\r
10564      int fromX;\r
10565      int fromY;\r
10566      int toX;\r
10567      int toY;\r
10568 {\r
10569   ChessSquare piece;\r
10570   POINT start, finish, mid;\r
10571   POINT frames[kFactor * 2 + 1];\r
10572   int nFrames, n;\r
10573 \r
10574   if (!appData.animate) return;\r
10575   if (doingSizing) return;\r
10576   if (fromY < 0 || fromX < 0) return;\r
10577   piece = board[fromY][fromX];\r
10578   if (piece >= EmptySquare) return;\r
10579 \r
10580   ScreenSquare(fromX, fromY, &start);\r
10581   ScreenSquare(toX, toY, &finish);\r
10582 \r
10583   /* All pieces except knights move in straight line */\r
10584   if (piece != WhiteKnight && piece != BlackKnight) {\r
10585     mid.x = start.x + (finish.x - start.x) / 2;\r
10586     mid.y = start.y + (finish.y - start.y) / 2;\r
10587   } else {\r
10588     /* Knight: make diagonal movement then straight */\r
10589     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10590        mid.x = start.x + (finish.x - start.x) / 2;\r
10591        mid.y = finish.y;\r
10592      } else {\r
10593        mid.x = finish.x;\r
10594        mid.y = start.y + (finish.y - start.y) / 2;\r
10595      }\r
10596   }\r
10597   \r
10598   /* Don't use as many frames for very short moves */\r
10599   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10600     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10601   else\r
10602     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10603 \r
10604   animInfo.from.x = fromX;\r
10605   animInfo.from.y = fromY;\r
10606   animInfo.to.x = toX;\r
10607   animInfo.to.y = toY;\r
10608   animInfo.lastpos = start;\r
10609   animInfo.piece = piece;\r
10610   for (n = 0; n < nFrames; n++) {\r
10611     animInfo.pos = frames[n];\r
10612     DrawPosition(FALSE, NULL);\r
10613     animInfo.lastpos = animInfo.pos;\r
10614     Sleep(appData.animSpeed);\r
10615   }\r
10616   animInfo.pos = finish;\r
10617   DrawPosition(FALSE, NULL);\r
10618   animInfo.piece = EmptySquare;\r
10619 }\r
10620 \r
10621 /*      Convert board position to corner of screen rect and color       */\r
10622 \r
10623 static void\r
10624 ScreenSquare(column, row, pt)\r
10625      int column; int row; POINT * pt;\r
10626 {\r
10627   if (flipView) {\r
10628     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10629     pt->y = lineGap + row * (squareSize + lineGap);\r
10630   } else {\r
10631     pt->x = lineGap + column * (squareSize + lineGap);\r
10632     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10633   }\r
10634 }\r
10635 \r
10636 /*      Generate a series of frame coords from start->mid->finish.\r
10637         The movement rate doubles until the half way point is\r
10638         reached, then halves back down to the final destination,\r
10639         which gives a nice slow in/out effect. The algorithmn\r
10640         may seem to generate too many intermediates for short\r
10641         moves, but remember that the purpose is to attract the\r
10642         viewers attention to the piece about to be moved and\r
10643         then to where it ends up. Too few frames would be less\r
10644         noticeable.                                             */\r
10645 \r
10646 static void\r
10647 Tween(start, mid, finish, factor, frames, nFrames)\r
10648      POINT * start; POINT * mid;\r
10649      POINT * finish; int factor;\r
10650      POINT frames[]; int * nFrames;\r
10651 {\r
10652   int n, fraction = 1, count = 0;\r
10653 \r
10654   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10655   for (n = 0; n < factor; n++)\r
10656     fraction *= 2;\r
10657   for (n = 0; n < factor; n++) {\r
10658     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10659     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10660     count ++;\r
10661     fraction = fraction / 2;\r
10662   }\r
10663   \r
10664   /* Midpoint */\r
10665   frames[count] = *mid;\r
10666   count ++;\r
10667   \r
10668   /* Slow out, stepping 1/2, then 1/4, ... */\r
10669   fraction = 2;\r
10670   for (n = 0; n < factor; n++) {\r
10671     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10672     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10673     count ++;\r
10674     fraction = fraction * 2;\r
10675   }\r
10676   *nFrames = count;\r
10677 }\r
10678 \r
10679 void\r
10680 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10681 {\r
10682 #if 0\r
10683     char buf[256];\r
10684 \r
10685     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10686         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10687 \r
10688     OutputDebugString( buf );\r
10689 #endif\r
10690 \r
10691     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10692 \r
10693     EvalGraphSet( first, last, current, pvInfoList );\r
10694 }\r
10695 \r
10696 void SetProgramStats( FrontEndProgramStats * stats )\r
10697 {\r
10698 #if 0\r
10699     char buf[1024];\r
10700 \r
10701     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10702         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10703 \r
10704     OutputDebugString( buf );\r
10705 #endif\r
10706 \r
10707     EngineOutputUpdate( stats );\r
10708 }\r