source clean up. Fix compiler warning, removed unused variables, etc.
[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 //  InitCommonControlsEx(&ex);\r
499   InitCommonControls();\r
500 \r
501   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
502   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
503   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
504 \r
505   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
506 \r
507   while (GetMessage(&msg, /* message structure */\r
508                     NULL, /* handle of window receiving the message */\r
509                     0,    /* lowest message to examine */\r
510                     0))   /* highest message to examine */\r
511     {\r
512       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
513           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
514           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
515           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
516           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
517           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
518           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
519           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
520           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
521           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
522         TranslateMessage(&msg); /* Translates virtual key codes */\r
523         DispatchMessage(&msg);  /* Dispatches message to window */\r
524       }\r
525     }\r
526 \r
527 \r
528   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
529 }\r
530 \r
531 /*---------------------------------------------------------------------------*\\r
532  *\r
533  * Initialization functions\r
534  *\r
535 \*---------------------------------------------------------------------------*/\r
536 \r
537 BOOL\r
538 InitApplication(HINSTANCE hInstance)\r
539 {\r
540   WNDCLASS wc;\r
541 \r
542   /* Fill in window class structure with parameters that describe the */\r
543   /* main window. */\r
544 \r
545   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
546   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
547   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
548   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
549   wc.hInstance     = hInstance;         /* Owner of this class */\r
550   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
551   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
552   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
553   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
554   wc.lpszClassName = szAppName;                 /* Name to register as */\r
555 \r
556   /* Register the window class and return success/failure code. */\r
557   if (!RegisterClass(&wc)) return FALSE;\r
558 \r
559   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
560   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
561   wc.cbClsExtra    = 0;\r
562   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
563   wc.hInstance     = hInstance;\r
564   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
565   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
566   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
567   wc.lpszMenuName  = NULL;\r
568   wc.lpszClassName = szConsoleName;\r
569 \r
570   if (!RegisterClass(&wc)) return FALSE;\r
571   return TRUE;\r
572 }\r
573 \r
574 \r
575 /* Set by InitInstance, used by EnsureOnScreen */\r
576 int screenHeight, screenWidth;\r
577 \r
578 void\r
579 EnsureOnScreen(int *x, int *y)\r
580 {\r
581 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
582   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
583   if (*x > screenWidth - 32) *x = 0;\r
584   if (*y > screenHeight - 32) *y = 0;\r
585   if (*x < 0) *x = 0;\r
586   if (*y < 0) *y = 0;\r
587 //  if (*x < 10) *x = 10;\r
588 //  if (*y < gap) *y = gap;\r
589 }\r
590 \r
591 BOOL\r
592 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
593 {\r
594   HWND hwnd; /* Main window handle. */\r
595   int ibs;\r
596   WINDOWPLACEMENT wp;\r
597   char *filepart;\r
598 \r
599   hInst = hInstance;    /* Store instance handle in our global variable */\r
600 \r
601   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
602     *filepart = NULLCHAR;\r
603   } else {\r
604     GetCurrentDirectory(MSG_SIZ, installDir);\r
605   }\r
606   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
607   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
608   if (appData.debugMode) {\r
609     debugFP = fopen(appData.nameOfDebugFile, "w");\r
610     setbuf(debugFP, NULL);\r
611   }\r
612 \r
613   InitBackEnd1();\r
614 \r
615 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
616 //  InitEngineUCI( installDir, &second );\r
617 \r
618   /* Create a main window for this application instance. */\r
619   hwnd = CreateWindow(szAppName, szTitle,\r
620                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
621                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
622                       NULL, NULL, hInstance, NULL);\r
623   hwndMain = hwnd;\r
624 \r
625   /* If window could not be created, return "failure" */\r
626   if (!hwnd) {\r
627     return (FALSE);\r
628   }\r
629 \r
630   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
631   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
632       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
633 \r
634       if (first.programLogo == NULL && appData.debugMode) {\r
635           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
636       }\r
637   } else if(appData.autoLogo) {\r
638       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
639         char buf[MSG_SIZ];\r
640         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
641         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
642       }\r
643   }\r
644 \r
645   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
646       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
647 \r
648       if (second.programLogo == NULL && appData.debugMode) {\r
649           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
650       }\r
651   } else if(appData.autoLogo) {\r
652       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
653         char buf[MSG_SIZ];\r
654         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
655         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
656       }\r
657   }\r
658 \r
659   iconWhite = LoadIcon(hInstance, "icon_white");\r
660   iconBlack = LoadIcon(hInstance, "icon_black");\r
661   iconCurrent = iconWhite;\r
662   InitDrawingColors();\r
663   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
664   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
665   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
666     /* Compute window size for each board size, and use the largest\r
667        size that fits on this screen as the default. */\r
668     InitDrawingSizes((BoardSize)ibs, 0);\r
669     if (boardSize == (BoardSize)-1 &&\r
670         winHeight <= screenHeight\r
671            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
672         && winWidth <= screenWidth) {\r
673       boardSize = (BoardSize)ibs;\r
674     }\r
675   }\r
676 \r
677   InitDrawingSizes(boardSize, 0);\r
678   InitMenuChecks();\r
679   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
680 \r
681   /* [AS] Load textures if specified */\r
682   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
683   \r
684   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
685       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
686       liteBackTextureMode = appData.liteBackTextureMode;\r
687 \r
688       if (liteBackTexture == NULL && appData.debugMode) {\r
689           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
690       }\r
691   }\r
692   \r
693   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
694       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       darkBackTextureMode = appData.darkBackTextureMode;\r
696 \r
697       if (darkBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
699       }\r
700   }\r
701 \r
702   mysrandom( (unsigned) time(NULL) );\r
703 \r
704   /* [AS] Restore layout */\r
705   if( wpMoveHistory.visible ) {\r
706       MoveHistoryPopUp();\r
707   }\r
708 \r
709   if( wpEvalGraph.visible ) {\r
710       EvalGraphPopUp();\r
711   }\r
712 \r
713   if( wpEngineOutput.visible ) {\r
714       EngineOutputPopUp();\r
715   }\r
716 \r
717   InitBackEnd2();\r
718 \r
719   /* Make the window visible; update its client area; and return "success" */\r
720   EnsureOnScreen(&boardX, &boardY);\r
721   wp.length = sizeof(WINDOWPLACEMENT);\r
722   wp.flags = 0;\r
723   wp.showCmd = nCmdShow;\r
724   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
725   wp.rcNormalPosition.left = boardX;\r
726   wp.rcNormalPosition.right = boardX + winWidth;\r
727   wp.rcNormalPosition.top = boardY;\r
728   wp.rcNormalPosition.bottom = boardY + winHeight;\r
729   SetWindowPlacement(hwndMain, &wp);\r
730 \r
731   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
732                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
733 \r
734 #if 0\r
735   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
736   if( gameInfo.variant != VariantFischeRandom ) {\r
737       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
738   }\r
739 #endif\r
740   if (hwndConsole) {\r
741 #if AOT_CONSOLE\r
742     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
743                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
744 #endif\r
745     ShowWindow(hwndConsole, nCmdShow);\r
746   }\r
747   UpdateWindow(hwnd);\r
748 \r
749   return TRUE;\r
750 \r
751 }\r
752 \r
753 \r
754 typedef enum {\r
755   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
756   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
757   ArgSettingsFilename\r
758 } ArgType;\r
759 \r
760 typedef struct {\r
761   char *argName;\r
762   ArgType argType;\r
763   /***\r
764   union {\r
765     String *pString;       // ArgString\r
766     int *pInt;             // ArgInt\r
767     float *pFloat;         // ArgFloat\r
768     Boolean *pBoolean;     // ArgBoolean\r
769     COLORREF *pColor;      // ArgColor\r
770     ColorClass cc;         // ArgAttribs\r
771     String *pFilename;     // ArgFilename\r
772     BoardSize *pBoardSize; // ArgBoardSize\r
773     int whichFont;         // ArgFont\r
774     DCB *pDCB;             // ArgCommSettings\r
775     String *pFilename;     // ArgSettingsFilename\r
776   } argLoc;\r
777   ***/\r
778   LPVOID argLoc;\r
779   BOOL save;\r
780 } ArgDescriptor;\r
781 \r
782 int junk;\r
783 ArgDescriptor argDescriptors[] = {\r
784   /* positional arguments */\r
785   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
786   { "", ArgNone, NULL },\r
787   /* keyword arguments */\r
788   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
789   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
790   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
791   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
792   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
793   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
794   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
795   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
796   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
797   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
798   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
799   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
800   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
801   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
802   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
803   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
804   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
805   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
806     FALSE },\r
807   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
808     FALSE },\r
809   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
810     FALSE },\r
811   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
812   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
813     FALSE },\r
814   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
815   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
816   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
817   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
818   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
819   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
820   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
821   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
822   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
823   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
824   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
825   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
826   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
827   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
828   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
829   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
830   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
831   /*!!bitmapDirectory?*/\r
832   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
833   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
834   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
835   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
836   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
837   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
838   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
839   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
840   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
841   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
842   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
843   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
844   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
845   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
846   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
847   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
848   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
849   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
850   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
851   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
852   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
853   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
854   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
855   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
856   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
857   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
858   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
859   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
860   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
861   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
862   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
863   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
864   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
865   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
866   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
867   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
868   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
869   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
870   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
871   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
872   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
873   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
874   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
875   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
876   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
877   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
878   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
879   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
880   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
881   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
882   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
883   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
884   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
885   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
886   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
887   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
888   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
889   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
890   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
891   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
892   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
893   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
894   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
895   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
896   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
897   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
898   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
899   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
900   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
901   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
902   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
903   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
904   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
905   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
906   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
907   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
908   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
909   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
910   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
911   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
912   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
913   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
914   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
915   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
916   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
917   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
918   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
919   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
920   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
921   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
922   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
923   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
924   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
925   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
926     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
927   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
928   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
929   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
930   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
931   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
932   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
933   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
934   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
935     TRUE }, /* must come after all fonts */\r
936   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
937   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
938     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
939   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
940   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
941   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
942   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
943   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
944   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
945   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
946   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
947   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
948   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
949   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
950   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
951   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
952   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
953   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
954   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
955   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
956   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
957   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
958   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
959   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
960   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
961   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
962   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
963   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
964   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
965   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
966   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
967 #if 0\r
968   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
969   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
970 #endif\r
971   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
972   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
973   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
974   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
975   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
976   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
977   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
978   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
979   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
980   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
981   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
982   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
983   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
984   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
985   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
986   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
987   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
988   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
989   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
990   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
991   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
992   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
993   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
994   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
995   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
996   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
997   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
998   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
999   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1000   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1001   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1002   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1003   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1004   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1005   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1006   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1007   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1008   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1009   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1010   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1011   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1012   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1013   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1014   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1015   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1016   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1017   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1018   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1019   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1020   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1021   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1022   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1023   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1024   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1025   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1026   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1027   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1028   { "highlightLastMove", ArgBoolean,\r
1029     (LPVOID) &appData.highlightLastMove, TRUE },\r
1030   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1031   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1032   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1033   { "highlightDragging", ArgBoolean,\r
1034     (LPVOID) &appData.highlightDragging, TRUE },\r
1035   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1036   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1037   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1038   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1039   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1040   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1041   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1042   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1043   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1044   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1045   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1046   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1047   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1048   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1049   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1050   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1051   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1052   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1053   { "soundShout", ArgFilename,\r
1054     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1055   { "soundSShout", ArgFilename,\r
1056     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1057   { "soundChannel1", ArgFilename,\r
1058     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1059   { "soundChannel", ArgFilename,\r
1060     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1061   { "soundKibitz", ArgFilename,\r
1062     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1063   { "soundTell", ArgFilename,\r
1064     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1065   { "soundChallenge", ArgFilename,\r
1066     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1067   { "soundRequest", ArgFilename,\r
1068     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1069   { "soundSeek", ArgFilename,\r
1070     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1071   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1072   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1073   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1074   { "soundIcsLoss", ArgFilename, \r
1075     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1076   { "soundIcsDraw", ArgFilename, \r
1077     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1078   { "soundIcsUnfinished", ArgFilename, \r
1079     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1080   { "soundIcsAlarm", ArgFilename, \r
1081     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1082   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1083   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1084   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1085   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1086   { "reuseChessPrograms", ArgBoolean,\r
1087     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1088   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1089   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1090   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1091   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1092   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1093   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1094   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1095   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1096   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1097   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1098   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1099   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1100   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1101   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1102   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1103   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1104   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1105   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1106   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1107   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1108   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1109   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1110   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1111   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1112   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1113   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1114   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1115   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1116   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1117   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1118   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1119   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1120   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1121   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1122   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1123   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1124   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1125     TRUE },\r
1126   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1127     TRUE },\r
1128   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1129   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1130   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1131   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1132   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1133   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1134   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1135   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1136   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1137   /* [AS] New features */\r
1138   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1139   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1140   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1141   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1142   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1143   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1144   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1145   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1146   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1147   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1148   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1149   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1150   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1151   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1152   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1153   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1154   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1155   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1156   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1157   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1158   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1159   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1160   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1161   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1162   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1163   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1164   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1165   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1166   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1167   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1168   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1169   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1170   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1171   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1172   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1173   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1174   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1175   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1176   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1177   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1178   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1179   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1180   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1181   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1182   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1183   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1184   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1185   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1186   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1187   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1188 \r
1189   /* [AS] Layout stuff */\r
1190   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1191   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1192   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1193   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1194   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1195 \r
1196   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1197   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1198   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1199   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1200   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1201 \r
1202   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1203   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1204   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1205   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1206   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1207 \r
1208   /* [HGM] board-size, adjudication and misc. options */\r
1209   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1210   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1211   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1212   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1213   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1214   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1215   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1216   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1217   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1218   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1219   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1220   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1221   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1222   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1223   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1224   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1225   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1226   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1227   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1228   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1229   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1230   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1231   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1232   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1233   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1234   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1235   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1236   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1237   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1238 \r
1239 #ifdef ZIPPY\r
1240   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1241   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1242   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1243   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1244   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1245   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1246   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1247   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1248   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1249   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1250   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1251   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1252   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1253     FALSE },\r
1254   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1255   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1256   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1257   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1258   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1259   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1260   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1261     FALSE },\r
1262   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1263   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1264   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1265   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1266   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1267   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1268   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1269   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1270   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1271   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1272   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1273   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1274   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1275   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1276   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1277   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1278   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1279   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1280 #endif\r
1281   /* [HGM] options for broadcasting and time odds */\r
1282   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1283   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1284   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1285   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1286   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1287   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1288   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1289   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1290   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1291   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1292   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1293   { NULL, ArgNone, NULL, FALSE }\r
1294 };\r
1295 \r
1296 \r
1297 /* Kludge for indirection files on command line */\r
1298 char* lastIndirectionFilename;\r
1299 ArgDescriptor argDescriptorIndirection =\r
1300 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1301 \r
1302 \r
1303 VOID\r
1304 ExitArgError(char *msg, char *badArg)\r
1305 {\r
1306   char buf[MSG_SIZ];\r
1307 \r
1308   sprintf(buf, "%s %s", msg, badArg);\r
1309   DisplayFatalError(buf, 0, 2);\r
1310   exit(2);\r
1311 }\r
1312 \r
1313 /* Command line font name parser.  NULL name means do nothing.\r
1314    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1315    For backward compatibility, syntax without the colon is also\r
1316    accepted, but font names with digits in them won't work in that case.\r
1317 */\r
1318 VOID\r
1319 ParseFontName(char *name, MyFontParams *mfp)\r
1320 {\r
1321   char *p, *q;\r
1322   if (name == NULL) return;\r
1323   p = name;\r
1324   q = strchr(p, ':');\r
1325   if (q) {\r
1326     if (q - p >= sizeof(mfp->faceName))\r
1327       ExitArgError("Font name too long:", name);\r
1328     memcpy(mfp->faceName, p, q - p);\r
1329     mfp->faceName[q - p] = NULLCHAR;\r
1330     p = q + 1;\r
1331   } else {\r
1332     q = mfp->faceName;\r
1333     while (*p && !isdigit(*p)) {\r
1334       *q++ = *p++;\r
1335       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1336         ExitArgError("Font name too long:", name);\r
1337     }\r
1338     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1339     *q = NULLCHAR;\r
1340   }\r
1341   if (!*p) ExitArgError("Font point size missing:", name);\r
1342   mfp->pointSize = (float) atof(p);\r
1343   mfp->bold = (strchr(p, 'b') != NULL);\r
1344   mfp->italic = (strchr(p, 'i') != NULL);\r
1345   mfp->underline = (strchr(p, 'u') != NULL);\r
1346   mfp->strikeout = (strchr(p, 's') != NULL);\r
1347 }\r
1348 \r
1349 /* Color name parser.\r
1350    X version accepts X color names, but this one\r
1351    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1352 COLORREF\r
1353 ParseColorName(char *name)\r
1354 {\r
1355   int red, green, blue, count;\r
1356   char buf[MSG_SIZ];\r
1357 \r
1358   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1359   if (count != 3) {\r
1360     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1361       &red, &green, &blue);\r
1362   }\r
1363   if (count != 3) {\r
1364     sprintf(buf, "Can't parse color name %s", name);\r
1365     DisplayError(buf, 0);\r
1366     return RGB(0, 0, 0);\r
1367   }\r
1368   return PALETTERGB(red, green, blue);\r
1369 }\r
1370 \r
1371 \r
1372 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1373 {\r
1374   char *e = argValue;\r
1375   int eff = 0;\r
1376 \r
1377   while (*e) {\r
1378     if (*e == 'b')      eff |= CFE_BOLD;\r
1379     else if (*e == 'i') eff |= CFE_ITALIC;\r
1380     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1381     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1382     else if (*e == '#' || isdigit(*e)) break;\r
1383     e++;\r
1384   }\r
1385   *effects = eff;\r
1386   *color   = ParseColorName(e);\r
1387 }\r
1388 \r
1389 \r
1390 BoardSize\r
1391 ParseBoardSize(char *name)\r
1392 {\r
1393   BoardSize bs = SizeTiny;\r
1394   while (sizeInfo[bs].name != NULL) {\r
1395     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1396     bs++;\r
1397   }\r
1398   ExitArgError("Unrecognized board size value", name);\r
1399   return bs; /* not reached */\r
1400 }\r
1401 \r
1402 \r
1403 char\r
1404 StringGet(void *getClosure)\r
1405 {\r
1406   char **p = (char **) getClosure;\r
1407   return *((*p)++);\r
1408 }\r
1409 \r
1410 char\r
1411 FileGet(void *getClosure)\r
1412 {\r
1413   int c;\r
1414   FILE* f = (FILE*) getClosure;\r
1415 \r
1416   c = getc(f);\r
1417   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1418   if (c == EOF)\r
1419     return NULLCHAR;\r
1420   else\r
1421     return (char) c;\r
1422 }\r
1423 \r
1424 /* Parse settings file named "name". If file found, return the\r
1425    full name in fullname and return TRUE; else return FALSE */\r
1426 BOOLEAN\r
1427 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1428 {\r
1429   char *dummy;\r
1430   FILE *f;\r
1431 \r
1432   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1433     f = fopen(fullname, "r");\r
1434     if (f != NULL) {\r
1435       ParseArgs(FileGet, f);\r
1436       fclose(f);\r
1437       return TRUE;\r
1438     }\r
1439   }\r
1440   return FALSE;\r
1441 }\r
1442 \r
1443 VOID\r
1444 ParseArgs(GetFunc get, void *cl)\r
1445 {\r
1446   char argName[ARG_MAX];\r
1447   char argValue[ARG_MAX];\r
1448   ArgDescriptor *ad;\r
1449   char start;\r
1450   char *q;\r
1451   int i, octval;\r
1452   char ch;\r
1453   int posarg = 0;\r
1454 \r
1455   ch = get(cl);\r
1456   for (;;) {\r
1457     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1458     if (ch == NULLCHAR) break;\r
1459     if (ch == ';') {\r
1460       /* Comment to end of line */\r
1461       ch = get(cl);\r
1462       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1463       continue;\r
1464     } else if (ch == '/' || ch == '-') {\r
1465       /* Switch */\r
1466       q = argName;\r
1467       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1468              ch != '\n' && ch != '\t') {\r
1469         *q++ = ch;\r
1470         ch = get(cl);\r
1471       }\r
1472       *q = NULLCHAR;\r
1473 \r
1474       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1475         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1476 \r
1477       if (ad->argName == NULL)\r
1478         ExitArgError("Unrecognized argument", argName);\r
1479 \r
1480     } else if (ch == '@') {\r
1481       /* Indirection file */\r
1482       ad = &argDescriptorIndirection;\r
1483       ch = get(cl);\r
1484     } else {\r
1485       /* Positional argument */\r
1486       ad = &argDescriptors[posarg++];\r
1487       strcpy(argName, ad->argName);\r
1488     }\r
1489 \r
1490     if (ad->argType == ArgTrue) {\r
1491       *(Boolean *) ad->argLoc = TRUE;\r
1492       continue;\r
1493     }\r
1494     if (ad->argType == ArgFalse) {\r
1495       *(Boolean *) ad->argLoc = FALSE;\r
1496       continue;\r
1497     }\r
1498 \r
1499     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1500     if (ch == NULLCHAR || ch == '\n') {\r
1501       ExitArgError("No value provided for argument", argName);\r
1502     }\r
1503     q = argValue;\r
1504     if (ch == '{') {\r
1505       // Quoting with { }.  No characters have to (or can) be escaped.\r
1506       // Thus the string cannot contain a '}' character.\r
1507       start = ch;\r
1508       ch = get(cl);\r
1509       while (start) {\r
1510         switch (ch) {\r
1511         case NULLCHAR:\r
1512           start = NULLCHAR;\r
1513           break;\r
1514           \r
1515         case '}':\r
1516           ch = get(cl);\r
1517           start = NULLCHAR;\r
1518           break;\r
1519 \r
1520         default:\r
1521           *q++ = ch;\r
1522           ch = get(cl);\r
1523           break;\r
1524         }\r
1525       }   \r
1526     } else if (ch == '\'' || ch == '"') {\r
1527       // Quoting with ' ' or " ", with \ as escape character.\r
1528       // Inconvenient for long strings that may contain Windows filenames.\r
1529       start = ch;\r
1530       ch = get(cl);\r
1531       while (start) {\r
1532         switch (ch) {\r
1533         case NULLCHAR:\r
1534           start = NULLCHAR;\r
1535           break;\r
1536 \r
1537         default:\r
1538         not_special:\r
1539           *q++ = ch;\r
1540           ch = get(cl);\r
1541           break;\r
1542 \r
1543         case '\'':\r
1544         case '\"':\r
1545           if (ch == start) {\r
1546             ch = get(cl);\r
1547             start = NULLCHAR;\r
1548             break;\r
1549           } else {\r
1550             goto not_special;\r
1551           }\r
1552 \r
1553         case '\\':\r
1554           if (ad->argType == ArgFilename\r
1555               || ad->argType == ArgSettingsFilename) {\r
1556               goto not_special;\r
1557           }\r
1558           ch = get(cl);\r
1559           switch (ch) {\r
1560           case NULLCHAR:\r
1561             ExitArgError("Incomplete \\ escape in value for", argName);\r
1562             break;\r
1563           case 'n':\r
1564             *q++ = '\n';\r
1565             ch = get(cl);\r
1566             break;\r
1567           case 'r':\r
1568             *q++ = '\r';\r
1569             ch = get(cl);\r
1570             break;\r
1571           case 't':\r
1572             *q++ = '\t';\r
1573             ch = get(cl);\r
1574             break;\r
1575           case 'b':\r
1576             *q++ = '\b';\r
1577             ch = get(cl);\r
1578             break;\r
1579           case 'f':\r
1580             *q++ = '\f';\r
1581             ch = get(cl);\r
1582             break;\r
1583           default:\r
1584             octval = 0;\r
1585             for (i = 0; i < 3; i++) {\r
1586               if (ch >= '0' && ch <= '7') {\r
1587                 octval = octval*8 + (ch - '0');\r
1588                 ch = get(cl);\r
1589               } else {\r
1590                 break;\r
1591               }\r
1592             }\r
1593             if (i > 0) {\r
1594               *q++ = (char) octval;\r
1595             } else {\r
1596               *q++ = ch;\r
1597               ch = get(cl);\r
1598             }\r
1599             break;\r
1600           }\r
1601           break;\r
1602         }\r
1603       }\r
1604     } else {\r
1605       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1606         *q++ = ch;\r
1607         ch = get(cl);\r
1608       }\r
1609     }\r
1610     *q = NULLCHAR;\r
1611 \r
1612     switch (ad->argType) {\r
1613     case ArgInt:\r
1614       *(int *) ad->argLoc = atoi(argValue);\r
1615       break;\r
1616 \r
1617     case ArgFloat:\r
1618       *(float *) ad->argLoc = (float) atof(argValue);\r
1619       break;\r
1620 \r
1621     case ArgString:\r
1622     case ArgFilename:\r
1623       *(char **) ad->argLoc = strdup(argValue);\r
1624       break;\r
1625 \r
1626     case ArgSettingsFilename:\r
1627       {\r
1628         char fullname[MSG_SIZ];\r
1629         if (ParseSettingsFile(argValue, fullname)) {\r
1630           if (ad->argLoc != NULL) {\r
1631             *(char **) ad->argLoc = strdup(fullname);\r
1632           }\r
1633         } else {\r
1634           if (ad->argLoc != NULL) {\r
1635           } else {\r
1636             ExitArgError("Failed to open indirection file", argValue);\r
1637           }\r
1638         }\r
1639       }\r
1640       break;\r
1641 \r
1642     case ArgBoolean:\r
1643       switch (argValue[0]) {\r
1644       case 't':\r
1645       case 'T':\r
1646         *(Boolean *) ad->argLoc = TRUE;\r
1647         break;\r
1648       case 'f':\r
1649       case 'F':\r
1650         *(Boolean *) ad->argLoc = FALSE;\r
1651         break;\r
1652       default:\r
1653         ExitArgError("Unrecognized boolean argument value", argValue);\r
1654         break;\r
1655       }\r
1656       break;\r
1657 \r
1658     case ArgColor:\r
1659       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1660       break;\r
1661 \r
1662     case ArgAttribs: {\r
1663       ColorClass cc = (ColorClass)ad->argLoc;\r
1664       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1665       }\r
1666       break;\r
1667       \r
1668     case ArgBoardSize:\r
1669       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1670       break;\r
1671 \r
1672     case ArgFont:\r
1673       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1674       break;\r
1675 \r
1676     case ArgCommSettings:\r
1677       ParseCommSettings(argValue, &dcb);\r
1678       break;\r
1679 \r
1680     case ArgNone:\r
1681       ExitArgError("Unrecognized argument", argValue);\r
1682       break;\r
1683     case ArgTrue:\r
1684     case ArgFalse: ;\r
1685     }\r
1686   }\r
1687 }\r
1688 \r
1689 VOID\r
1690 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1691 {\r
1692   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1693   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1694   DeleteDC(hdc);\r
1695   lf->lfWidth = 0;\r
1696   lf->lfEscapement = 0;\r
1697   lf->lfOrientation = 0;\r
1698   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1699   lf->lfItalic = mfp->italic;\r
1700   lf->lfUnderline = mfp->underline;\r
1701   lf->lfStrikeOut = mfp->strikeout;\r
1702   lf->lfCharSet = DEFAULT_CHARSET;\r
1703   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1704   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1705   lf->lfQuality = DEFAULT_QUALITY;\r
1706   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1707   strcpy(lf->lfFaceName, mfp->faceName);\r
1708 }\r
1709 \r
1710 VOID\r
1711 CreateFontInMF(MyFont *mf)\r
1712 {\r
1713   LFfromMFP(&mf->lf, &mf->mfp);\r
1714   if (mf->hf) DeleteObject(mf->hf);\r
1715   mf->hf = CreateFontIndirect(&mf->lf);\r
1716 }\r
1717 \r
1718 VOID\r
1719 SetDefaultTextAttribs()\r
1720 {\r
1721   ColorClass cc;\r
1722   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1723     ParseAttribs(&textAttribs[cc].color, \r
1724                  &textAttribs[cc].effects, \r
1725                  defaultTextAttribs[cc]);\r
1726   }\r
1727 }\r
1728 \r
1729 VOID\r
1730 SetDefaultSounds()\r
1731 {\r
1732   ColorClass cc;\r
1733   SoundClass sc;\r
1734   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1735     textAttribs[cc].sound.name = strdup("");\r
1736     textAttribs[cc].sound.data = NULL;\r
1737   }\r
1738   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1739     sounds[sc].name = strdup("");\r
1740     sounds[sc].data = NULL;\r
1741   }\r
1742   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1743 }\r
1744 \r
1745 VOID\r
1746 LoadAllSounds()\r
1747 {\r
1748   ColorClass cc;\r
1749   SoundClass sc;\r
1750   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1751     MyLoadSound(&textAttribs[cc].sound);\r
1752   }\r
1753   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1754     MyLoadSound(&sounds[sc]);\r
1755   }\r
1756 }\r
1757 \r
1758 VOID\r
1759 InitAppData(LPSTR lpCmdLine)\r
1760 {\r
1761   int i, j;\r
1762   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1763   char *dummy, *p;\r
1764 \r
1765   programName = szAppName;\r
1766 \r
1767   /* Initialize to defaults */\r
1768   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1769   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1770   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1771   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1772   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1773   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1774   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1775   SetDefaultTextAttribs();\r
1776   SetDefaultSounds();\r
1777   appData.movesPerSession = MOVES_PER_SESSION;\r
1778   appData.initString = INIT_STRING;\r
1779   appData.secondInitString = INIT_STRING;\r
1780   appData.firstComputerString = COMPUTER_STRING;\r
1781   appData.secondComputerString = COMPUTER_STRING;\r
1782   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1783   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1784   appData.firstPlaysBlack = FALSE;\r
1785   appData.noChessProgram = FALSE;\r
1786   chessProgram = FALSE;\r
1787   appData.firstHost = FIRST_HOST;\r
1788   appData.secondHost = SECOND_HOST;\r
1789   appData.firstDirectory = FIRST_DIRECTORY;\r
1790   appData.secondDirectory = SECOND_DIRECTORY;\r
1791   appData.bitmapDirectory = "";\r
1792   appData.remoteShell = REMOTE_SHELL;\r
1793   appData.remoteUser = "";\r
1794   appData.timeDelay = TIME_DELAY;\r
1795   appData.timeControl = TIME_CONTROL;\r
1796   appData.timeIncrement = TIME_INCREMENT;\r
1797   appData.icsActive = FALSE;\r
1798   appData.icsHost = "";\r
1799   appData.icsPort = ICS_PORT;\r
1800   appData.icsCommPort = ICS_COMM_PORT;\r
1801   appData.icsLogon = ICS_LOGON;\r
1802   appData.icsHelper = "";\r
1803   appData.useTelnet = FALSE;\r
1804   appData.telnetProgram = TELNET_PROGRAM;\r
1805   appData.gateway = "";\r
1806   appData.loadGameFile = "";\r
1807   appData.loadGameIndex = 0;\r
1808   appData.saveGameFile = "";\r
1809   appData.autoSaveGames = FALSE;\r
1810   appData.loadPositionFile = "";\r
1811   appData.loadPositionIndex = 1;\r
1812   appData.savePositionFile = "";\r
1813   appData.matchMode = FALSE;\r
1814   appData.matchGames = 0;\r
1815   appData.monoMode = FALSE;\r
1816   appData.debugMode = FALSE;\r
1817   appData.clockMode = TRUE;\r
1818   boardSize = (BoardSize) -1; /* determine by screen size */\r
1819   appData.Iconic = FALSE; /*unused*/\r
1820   appData.searchTime = "";\r
1821   appData.searchDepth = 0;\r
1822   appData.showCoords = FALSE;\r
1823   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1824   appData.autoCallFlag = FALSE;\r
1825   appData.flipView = FALSE;\r
1826   appData.autoFlipView = TRUE;\r
1827   appData.cmailGameName = "";\r
1828   appData.alwaysPromoteToQueen = FALSE;\r
1829   appData.oldSaveStyle = FALSE;\r
1830   appData.quietPlay = FALSE;\r
1831   appData.showThinking = FALSE;\r
1832   appData.ponderNextMove = TRUE;\r
1833   appData.periodicUpdates = TRUE;\r
1834   appData.popupExitMessage = TRUE;\r
1835   appData.popupMoveErrors = FALSE;\r
1836   appData.autoObserve = FALSE;\r
1837   appData.autoComment = FALSE;\r
1838   appData.animate = TRUE;\r
1839   appData.animSpeed = 10;\r
1840   appData.animateDragging = TRUE;\r
1841   appData.highlightLastMove = TRUE;\r
1842   appData.getMoveList = TRUE;\r
1843   appData.testLegality = TRUE;\r
1844   appData.premove = TRUE;\r
1845   appData.premoveWhite = FALSE;\r
1846   appData.premoveWhiteText = "";\r
1847   appData.premoveBlack = FALSE;\r
1848   appData.premoveBlackText = "";\r
1849   appData.icsAlarm = TRUE;\r
1850   appData.icsAlarmTime = 5000;\r
1851   appData.autoRaiseBoard = TRUE;\r
1852   appData.localLineEditing = TRUE;\r
1853   appData.colorize = TRUE;\r
1854   appData.reuseFirst = TRUE;\r
1855   appData.reuseSecond = TRUE;\r
1856   appData.blindfold = FALSE;\r
1857   appData.icsEngineAnalyze = FALSE;\r
1858   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1859   dcb.DCBlength = sizeof(DCB);\r
1860   dcb.BaudRate = 9600;\r
1861   dcb.fBinary = TRUE;\r
1862   dcb.fParity = FALSE;\r
1863   dcb.fOutxCtsFlow = FALSE;\r
1864   dcb.fOutxDsrFlow = FALSE;\r
1865   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1866   dcb.fDsrSensitivity = FALSE;\r
1867   dcb.fTXContinueOnXoff = TRUE;\r
1868   dcb.fOutX = FALSE;\r
1869   dcb.fInX = FALSE;\r
1870   dcb.fNull = FALSE;\r
1871   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1872   dcb.fAbortOnError = FALSE;\r
1873   dcb.ByteSize = 7;\r
1874   dcb.Parity = SPACEPARITY;\r
1875   dcb.StopBits = ONESTOPBIT;\r
1876   settingsFileName = SETTINGS_FILE;\r
1877   saveSettingsOnExit = TRUE;\r
1878   boardX = CW_USEDEFAULT;\r
1879   boardY = CW_USEDEFAULT;\r
1880   consoleX = CW_USEDEFAULT; \r
1881   consoleY = CW_USEDEFAULT; \r
1882   consoleW = CW_USEDEFAULT;\r
1883   consoleH = CW_USEDEFAULT;\r
1884   analysisX = CW_USEDEFAULT; \r
1885   analysisY = CW_USEDEFAULT; \r
1886   analysisW = CW_USEDEFAULT;\r
1887   analysisH = CW_USEDEFAULT;\r
1888   commentX = CW_USEDEFAULT; \r
1889   commentY = CW_USEDEFAULT; \r
1890   commentW = CW_USEDEFAULT;\r
1891   commentH = CW_USEDEFAULT;\r
1892   editTagsX = CW_USEDEFAULT; \r
1893   editTagsY = CW_USEDEFAULT; \r
1894   editTagsW = CW_USEDEFAULT;\r
1895   editTagsH = CW_USEDEFAULT;\r
1896   gameListX = CW_USEDEFAULT; \r
1897   gameListY = CW_USEDEFAULT; \r
1898   gameListW = CW_USEDEFAULT;\r
1899   gameListH = CW_USEDEFAULT;\r
1900   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1901   icsNames = ICS_NAMES;\r
1902   firstChessProgramNames = FCP_NAMES;\r
1903   secondChessProgramNames = SCP_NAMES;\r
1904   appData.initialMode = "";\r
1905   appData.variant = "normal";\r
1906   appData.firstProtocolVersion = PROTOVER;\r
1907   appData.secondProtocolVersion = PROTOVER;\r
1908   appData.showButtonBar = TRUE;\r
1909 \r
1910    /* [AS] New properties (see comments in header file) */\r
1911   appData.firstScoreIsAbsolute = FALSE;\r
1912   appData.secondScoreIsAbsolute = FALSE;\r
1913   appData.saveExtendedInfoInPGN = FALSE;\r
1914   appData.hideThinkingFromHuman = FALSE;\r
1915   appData.liteBackTextureFile = "";\r
1916   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1917   appData.darkBackTextureFile = "";\r
1918   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1919   appData.renderPiecesWithFont = "";\r
1920   appData.fontToPieceTable = "";\r
1921   appData.fontBackColorWhite = 0;\r
1922   appData.fontForeColorWhite = 0;\r
1923   appData.fontBackColorBlack = 0;\r
1924   appData.fontForeColorBlack = 0;\r
1925   appData.fontPieceSize = 80;\r
1926   appData.overrideLineGap = 1;\r
1927   appData.adjudicateLossThreshold = 0;\r
1928   appData.delayBeforeQuit = 0;\r
1929   appData.delayAfterQuit = 0;\r
1930   appData.nameOfDebugFile = "winboard.debug";\r
1931   appData.pgnEventHeader = "Computer Chess Game";\r
1932   appData.defaultFrcPosition = -1;\r
1933   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1934   appData.saveOutOfBookInfo = TRUE;\r
1935   appData.showEvalInMoveHistory = TRUE;\r
1936   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1937   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1938   appData.highlightMoveWithArrow = FALSE;\r
1939   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1940   appData.useStickyWindows = TRUE;\r
1941   appData.adjudicateDrawMoves = 0;\r
1942   appData.autoDisplayComment = TRUE;\r
1943   appData.autoDisplayTags = TRUE;\r
1944   appData.firstIsUCI = FALSE;\r
1945   appData.secondIsUCI = FALSE;\r
1946   appData.firstHasOwnBookUCI = TRUE;\r
1947   appData.secondHasOwnBookUCI = TRUE;\r
1948   appData.polyglotDir = "";\r
1949   appData.usePolyglotBook = FALSE;\r
1950   appData.polyglotBook = "";\r
1951   appData.defaultHashSize = 64;\r
1952   appData.defaultCacheSizeEGTB = 4;\r
1953   appData.defaultPathEGTB = "c:\\egtb";\r
1954   appData.firstOptions = "";\r
1955   appData.secondOptions = "";\r
1956 \r
1957   InitWindowPlacement( &wpMoveHistory );\r
1958   InitWindowPlacement( &wpEvalGraph );\r
1959   InitWindowPlacement( &wpEngineOutput );\r
1960 \r
1961   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1962   appData.NrFiles      = -1;\r
1963   appData.NrRanks      = -1;\r
1964   appData.holdingsSize = -1;\r
1965   appData.testClaims   = FALSE;\r
1966   appData.checkMates   = FALSE;\r
1967   appData.materialDraws= FALSE;\r
1968   appData.trivialDraws = FALSE;\r
1969   appData.ruleMoves    = 51;\r
1970   appData.drawRepeats  = 6;\r
1971   appData.matchPause   = 10000;\r
1972   appData.alphaRank    = FALSE;\r
1973   appData.allWhite     = FALSE;\r
1974   appData.upsideDown   = FALSE;\r
1975   appData.serverPause  = 15;\r
1976   appData.serverMovesName   = NULL;\r
1977   appData.suppressLoadMoves = FALSE;\r
1978   appData.firstTimeOdds  = 1;\r
1979   appData.secondTimeOdds = 1;\r
1980   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1981   appData.secondAccumulateTC = 1;\r
1982   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1983   appData.secondNPS = -1;\r
1984   appData.engineComments = 1;\r
1985   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1986   appData.egtFormats = "";\r
1987 \r
1988 #ifdef ZIPPY\r
1989   appData.zippyTalk = ZIPPY_TALK;\r
1990   appData.zippyPlay = ZIPPY_PLAY;\r
1991   appData.zippyLines = ZIPPY_LINES;\r
1992   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1993   appData.zippyPassword = ZIPPY_PASSWORD;\r
1994   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1995   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1996   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1997   appData.zippyUseI = ZIPPY_USE_I;\r
1998   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1999   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2000   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2001   appData.zippyGameStart = ZIPPY_GAME_START;\r
2002   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2003   appData.zippyAbort = ZIPPY_ABORT;\r
2004   appData.zippyVariants = ZIPPY_VARIANTS;\r
2005   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2006   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2007 #endif\r
2008 \r
2009   /* Point font array elements to structures and\r
2010      parse default font names */\r
2011   for (i=0; i<NUM_FONTS; i++) {\r
2012     for (j=0; j<NUM_SIZES; j++) {\r
2013       font[j][i] = &fontRec[j][i];\r
2014       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2015     }\r
2016   }\r
2017   \r
2018   /* Parse default settings file if any */\r
2019   if (ParseSettingsFile(settingsFileName, buf)) {\r
2020     settingsFileName = strdup(buf);\r
2021   }\r
2022 \r
2023   /* Parse command line */\r
2024   ParseArgs(StringGet, &lpCmdLine);\r
2025 \r
2026   /* [HGM] make sure board size is acceptable */\r
2027   if(appData.NrFiles > BOARD_SIZE ||\r
2028      appData.NrRanks > BOARD_SIZE   )\r
2029       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2030 \r
2031   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2032    * with options from the command line, we now make an even higher priority\r
2033    * overrule by WB options attached to the engine command line. This so that\r
2034    * tournament managers can use WB options (such as /timeOdds) that follow\r
2035    * the engines.\r
2036    */\r
2037   if(appData.firstChessProgram != NULL) {\r
2038       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2039       static char *f = "first";\r
2040       char buf[MSG_SIZ], *q = buf;\r
2041       if(p != NULL) { // engine command line contains WinBoard options\r
2042           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2043           ParseArgs(StringGet, &q);\r
2044           p[-1] = 0; // cut them offengine command line\r
2045       }\r
2046   }\r
2047   // now do same for second chess program\r
2048   if(appData.secondChessProgram != NULL) {\r
2049       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2050       static char *s = "second";\r
2051       char buf[MSG_SIZ], *q = buf;\r
2052       if(p != NULL) { // engine command line contains WinBoard options\r
2053           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2054           ParseArgs(StringGet, &q);\r
2055           p[-1] = 0; // cut them offengine command line\r
2056       }\r
2057   }\r
2058 \r
2059 \r
2060   /* Propagate options that affect others */\r
2061   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2062   if (appData.icsActive || appData.noChessProgram) {\r
2063      chessProgram = FALSE;  /* not local chess program mode */\r
2064   }\r
2065 \r
2066   /* Open startup dialog if needed */\r
2067   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2068       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2069       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2070                         *appData.secondChessProgram == NULLCHAR))) {\r
2071     FARPROC lpProc;\r
2072     \r
2073     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2074     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2075     FreeProcInstance(lpProc);\r
2076   }\r
2077 \r
2078   /* Make sure save files land in the right (?) directory */\r
2079   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2080     appData.saveGameFile = strdup(buf);\r
2081   }\r
2082   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2083     appData.savePositionFile = strdup(buf);\r
2084   }\r
2085 \r
2086   /* Finish initialization for fonts and sounds */\r
2087   for (i=0; i<NUM_FONTS; i++) {\r
2088     for (j=0; j<NUM_SIZES; j++) {\r
2089       CreateFontInMF(font[j][i]);\r
2090     }\r
2091   }\r
2092   /* xboard, and older WinBoards, controlled the move sound with the\r
2093      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2094      always turn the option on (so that the backend will call us),\r
2095      then let the user turn the sound off by setting it to silence if\r
2096      desired.  To accommodate old winboard.ini files saved by old\r
2097      versions of WinBoard, we also turn off the sound if the option\r
2098      was initially set to false. */\r
2099   if (!appData.ringBellAfterMoves) {\r
2100     sounds[(int)SoundMove].name = strdup("");\r
2101     appData.ringBellAfterMoves = TRUE;\r
2102   }\r
2103   GetCurrentDirectory(MSG_SIZ, currDir);\r
2104   SetCurrentDirectory(installDir);\r
2105   LoadAllSounds();\r
2106   SetCurrentDirectory(currDir);\r
2107 \r
2108   p = icsTextMenuString;\r
2109   if (p[0] == '@') {\r
2110     FILE* f = fopen(p + 1, "r");\r
2111     if (f == NULL) {\r
2112       DisplayFatalError(p + 1, errno, 2);\r
2113       return;\r
2114     }\r
2115     i = fread(buf, 1, sizeof(buf)-1, f);\r
2116     fclose(f);\r
2117     buf[i] = NULLCHAR;\r
2118     p = buf;\r
2119   }\r
2120   ParseIcsTextMenu(strdup(p));\r
2121 }\r
2122 \r
2123 \r
2124 VOID\r
2125 InitMenuChecks()\r
2126 {\r
2127   HMENU hmenu = GetMenu(hwndMain);\r
2128 \r
2129   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2130                         MF_BYCOMMAND|((appData.icsActive &&\r
2131                                        *appData.icsCommPort != NULLCHAR) ?\r
2132                                       MF_ENABLED : MF_GRAYED));\r
2133   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2134                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2135                                      MF_CHECKED : MF_UNCHECKED));\r
2136 }\r
2137 \r
2138 \r
2139 VOID\r
2140 SaveSettings(char* name)\r
2141 {\r
2142   FILE *f;\r
2143   ArgDescriptor *ad;\r
2144   WINDOWPLACEMENT wp;\r
2145   char dir[MSG_SIZ];\r
2146 \r
2147   if (!hwndMain) return;\r
2148 \r
2149   GetCurrentDirectory(MSG_SIZ, dir);\r
2150   SetCurrentDirectory(installDir);\r
2151   f = fopen(name, "w");\r
2152   SetCurrentDirectory(dir);\r
2153   if (f == NULL) {\r
2154     DisplayError(name, errno);\r
2155     return;\r
2156   }\r
2157   fprintf(f, ";\n");\r
2158   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2159   fprintf(f, ";\n");\r
2160   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2161   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2162   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2163   fprintf(f, ";\n");\r
2164 \r
2165   wp.length = sizeof(WINDOWPLACEMENT);\r
2166   GetWindowPlacement(hwndMain, &wp);\r
2167   boardX = wp.rcNormalPosition.left;\r
2168   boardY = wp.rcNormalPosition.top;\r
2169 \r
2170   if (hwndConsole) {\r
2171     GetWindowPlacement(hwndConsole, &wp);\r
2172     consoleX = wp.rcNormalPosition.left;\r
2173     consoleY = wp.rcNormalPosition.top;\r
2174     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2175     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2176   }\r
2177 \r
2178   if (analysisDialog) {\r
2179     GetWindowPlacement(analysisDialog, &wp);\r
2180     analysisX = wp.rcNormalPosition.left;\r
2181     analysisY = wp.rcNormalPosition.top;\r
2182     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2183     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2184   }\r
2185 \r
2186   if (commentDialog) {\r
2187     GetWindowPlacement(commentDialog, &wp);\r
2188     commentX = wp.rcNormalPosition.left;\r
2189     commentY = wp.rcNormalPosition.top;\r
2190     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2191     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2192   }\r
2193 \r
2194   if (editTagsDialog) {\r
2195     GetWindowPlacement(editTagsDialog, &wp);\r
2196     editTagsX = wp.rcNormalPosition.left;\r
2197     editTagsY = wp.rcNormalPosition.top;\r
2198     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2199     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2200   }\r
2201 \r
2202   if (gameListDialog) {\r
2203     GetWindowPlacement(gameListDialog, &wp);\r
2204     gameListX = wp.rcNormalPosition.left;\r
2205     gameListY = wp.rcNormalPosition.top;\r
2206     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2207     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2208   }\r
2209 \r
2210   /* [AS] Move history */\r
2211   wpMoveHistory.visible = MoveHistoryIsUp();\r
2212   \r
2213   if( moveHistoryDialog ) {\r
2214     GetWindowPlacement(moveHistoryDialog, &wp);\r
2215     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2216     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2217     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2218     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2219   }\r
2220 \r
2221   /* [AS] Eval graph */\r
2222   wpEvalGraph.visible = EvalGraphIsUp();\r
2223 \r
2224   if( evalGraphDialog ) {\r
2225     GetWindowPlacement(evalGraphDialog, &wp);\r
2226     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2227     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2228     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2229     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2230   }\r
2231 \r
2232   /* [AS] Engine output */\r
2233   wpEngineOutput.visible = EngineOutputIsUp();\r
2234 \r
2235   if( engineOutputDialog ) {\r
2236     GetWindowPlacement(engineOutputDialog, &wp);\r
2237     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2238     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2239     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2240     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2241   }\r
2242 \r
2243   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2244     if (!ad->save) continue;\r
2245     switch (ad->argType) {\r
2246     case ArgString:\r
2247       {\r
2248         char *p = *(char **)ad->argLoc;\r
2249         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2250           /* Quote multiline values or \-containing values\r
2251              with { } if possible */\r
2252           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2253         } else {\r
2254           /* Else quote with " " */\r
2255           fprintf(f, "/%s=\"", ad->argName);\r
2256           while (*p) {\r
2257             if (*p == '\n') fprintf(f, "\n");\r
2258             else if (*p == '\r') fprintf(f, "\\r");\r
2259             else if (*p == '\t') fprintf(f, "\\t");\r
2260             else if (*p == '\b') fprintf(f, "\\b");\r
2261             else if (*p == '\f') fprintf(f, "\\f");\r
2262             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2263             else if (*p == '\"') fprintf(f, "\\\"");\r
2264             else if (*p == '\\') fprintf(f, "\\\\");\r
2265             else putc(*p, f);\r
2266             p++;\r
2267           }\r
2268           fprintf(f, "\"\n");\r
2269         }\r
2270       }\r
2271       break;\r
2272     case ArgInt:\r
2273       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2274       break;\r
2275     case ArgFloat:\r
2276       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2277       break;\r
2278     case ArgBoolean:\r
2279       fprintf(f, "/%s=%s\n", ad->argName, \r
2280         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2281       break;\r
2282     case ArgTrue:\r
2283       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2284       break;\r
2285     case ArgFalse:\r
2286       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2287       break;\r
2288     case ArgColor:\r
2289       {\r
2290         COLORREF color = *(COLORREF *)ad->argLoc;\r
2291         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2292           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2293       }\r
2294       break;\r
2295     case ArgAttribs:\r
2296       {\r
2297         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2298         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2299           (ta->effects & CFE_BOLD) ? "b" : "",\r
2300           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2301           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2302           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2303           (ta->effects) ? " " : "",\r
2304           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2305       }\r
2306       break;\r
2307     case ArgFilename:\r
2308       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2309         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2310       } else {\r
2311         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2312       }\r
2313       break;\r
2314     case ArgBoardSize:\r
2315       fprintf(f, "/%s=%s\n", ad->argName,\r
2316               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2317       break;\r
2318     case ArgFont:\r
2319       {\r
2320         int bs;\r
2321         for (bs=0; bs<NUM_SIZES; bs++) {\r
2322           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2323           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2324           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2325             ad->argName, mfp->faceName, mfp->pointSize,\r
2326             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2327             mfp->bold ? "b" : "",\r
2328             mfp->italic ? "i" : "",\r
2329             mfp->underline ? "u" : "",\r
2330             mfp->strikeout ? "s" : "");\r
2331         }\r
2332       }\r
2333       break;\r
2334     case ArgCommSettings:\r
2335       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2336     case ArgNone:\r
2337     case ArgSettingsFilename: ;\r
2338     }\r
2339   }\r
2340   fclose(f);\r
2341 }\r
2342 \r
2343 \r
2344 \r
2345 /*---------------------------------------------------------------------------*\\r
2346  *\r
2347  * GDI board drawing routines\r
2348  *\r
2349 \*---------------------------------------------------------------------------*/\r
2350 \r
2351 /* [AS] Draw square using background texture */\r
2352 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2353 {\r
2354     XFORM   x;\r
2355 \r
2356     if( mode == 0 ) {\r
2357         return; /* Should never happen! */\r
2358     }\r
2359 \r
2360     SetGraphicsMode( dst, GM_ADVANCED );\r
2361 \r
2362     switch( mode ) {\r
2363     case 1:\r
2364         /* Identity */\r
2365         break;\r
2366     case 2:\r
2367         /* X reflection */\r
2368         x.eM11 = -1.0;\r
2369         x.eM12 = 0;\r
2370         x.eM21 = 0;\r
2371         x.eM22 = 1.0;\r
2372         x.eDx = (FLOAT) dw + dx - 1;\r
2373         x.eDy = 0;\r
2374         dx = 0;\r
2375         SetWorldTransform( dst, &x );\r
2376         break;\r
2377     case 3:\r
2378         /* Y reflection */\r
2379         x.eM11 = 1.0;\r
2380         x.eM12 = 0;\r
2381         x.eM21 = 0;\r
2382         x.eM22 = -1.0;\r
2383         x.eDx = 0;\r
2384         x.eDy = (FLOAT) dh + dy - 1;\r
2385         dy = 0;\r
2386         SetWorldTransform( dst, &x );\r
2387         break;\r
2388     case 4:\r
2389         /* X/Y flip */\r
2390         x.eM11 = 0;\r
2391         x.eM12 = 1.0;\r
2392         x.eM21 = 1.0;\r
2393         x.eM22 = 0;\r
2394         x.eDx = (FLOAT) dx;\r
2395         x.eDy = (FLOAT) dy;\r
2396         dx = 0;\r
2397         dy = 0;\r
2398         SetWorldTransform( dst, &x );\r
2399         break;\r
2400     }\r
2401 \r
2402     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2403 \r
2404     x.eM11 = 1.0;\r
2405     x.eM12 = 0;\r
2406     x.eM21 = 0;\r
2407     x.eM22 = 1.0;\r
2408     x.eDx = 0;\r
2409     x.eDy = 0;\r
2410     SetWorldTransform( dst, &x );\r
2411 \r
2412     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2413 }\r
2414 \r
2415 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2416 enum {\r
2417     PM_WP = (int) WhitePawn, \r
2418     PM_WN = (int) WhiteKnight, \r
2419     PM_WB = (int) WhiteBishop, \r
2420     PM_WR = (int) WhiteRook, \r
2421     PM_WQ = (int) WhiteQueen, \r
2422     PM_WF = (int) WhiteFerz, \r
2423     PM_WW = (int) WhiteWazir, \r
2424     PM_WE = (int) WhiteAlfil, \r
2425     PM_WM = (int) WhiteMan, \r
2426     PM_WO = (int) WhiteCannon, \r
2427     PM_WU = (int) WhiteUnicorn, \r
2428     PM_WH = (int) WhiteNightrider, \r
2429     PM_WA = (int) WhiteAngel, \r
2430     PM_WC = (int) WhiteMarshall, \r
2431     PM_WAB = (int) WhiteCardinal, \r
2432     PM_WD = (int) WhiteDragon, \r
2433     PM_WL = (int) WhiteLance, \r
2434     PM_WS = (int) WhiteCobra, \r
2435     PM_WV = (int) WhiteFalcon, \r
2436     PM_WSG = (int) WhiteSilver, \r
2437     PM_WG = (int) WhiteGrasshopper, \r
2438     PM_WK = (int) WhiteKing,\r
2439     PM_BP = (int) BlackPawn, \r
2440     PM_BN = (int) BlackKnight, \r
2441     PM_BB = (int) BlackBishop, \r
2442     PM_BR = (int) BlackRook, \r
2443     PM_BQ = (int) BlackQueen, \r
2444     PM_BF = (int) BlackFerz, \r
2445     PM_BW = (int) BlackWazir, \r
2446     PM_BE = (int) BlackAlfil, \r
2447     PM_BM = (int) BlackMan,\r
2448     PM_BO = (int) BlackCannon, \r
2449     PM_BU = (int) BlackUnicorn, \r
2450     PM_BH = (int) BlackNightrider, \r
2451     PM_BA = (int) BlackAngel, \r
2452     PM_BC = (int) BlackMarshall, \r
2453     PM_BG = (int) BlackGrasshopper, \r
2454     PM_BAB = (int) BlackCardinal,\r
2455     PM_BD = (int) BlackDragon,\r
2456     PM_BL = (int) BlackLance,\r
2457     PM_BS = (int) BlackCobra,\r
2458     PM_BV = (int) BlackFalcon,\r
2459     PM_BSG = (int) BlackSilver,\r
2460     PM_BK = (int) BlackKing\r
2461 };\r
2462 \r
2463 static HFONT hPieceFont = NULL;\r
2464 static HBITMAP hPieceMask[(int) EmptySquare];\r
2465 static HBITMAP hPieceFace[(int) EmptySquare];\r
2466 static int fontBitmapSquareSize = 0;\r
2467 static char pieceToFontChar[(int) EmptySquare] =\r
2468                               { 'p', 'n', 'b', 'r', 'q', \r
2469                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2470                       'k', 'o', 'm', 'v', 't', 'w', \r
2471                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2472                                                               'l' };\r
2473 \r
2474 extern BOOL SetCharTable( char *table, const char * map );\r
2475 /* [HGM] moved to backend.c */\r
2476 \r
2477 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2478 {\r
2479     HBRUSH hbrush;\r
2480     BYTE r1 = GetRValue( color );\r
2481     BYTE g1 = GetGValue( color );\r
2482     BYTE b1 = GetBValue( color );\r
2483     BYTE r2 = r1 / 2;\r
2484     BYTE g2 = g1 / 2;\r
2485     BYTE b2 = b1 / 2;\r
2486     RECT rc;\r
2487 \r
2488     /* Create a uniform background first */\r
2489     hbrush = CreateSolidBrush( color );\r
2490     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2491     FillRect( hdc, &rc, hbrush );\r
2492     DeleteObject( hbrush );\r
2493     \r
2494     if( mode == 1 ) {\r
2495         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2496         int steps = squareSize / 2;\r
2497         int i;\r
2498 \r
2499         for( i=0; i<steps; i++ ) {\r
2500             BYTE r = r1 - (r1-r2) * i / steps;\r
2501             BYTE g = g1 - (g1-g2) * i / steps;\r
2502             BYTE b = b1 - (b1-b2) * i / steps;\r
2503 \r
2504             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2505             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2506             FillRect( hdc, &rc, hbrush );\r
2507             DeleteObject(hbrush);\r
2508         }\r
2509     }\r
2510     else if( mode == 2 ) {\r
2511         /* Diagonal gradient, good more or less for every piece */\r
2512         POINT triangle[3];\r
2513         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2514         HBRUSH hbrush_old;\r
2515         int steps = squareSize;\r
2516         int i;\r
2517 \r
2518         triangle[0].x = squareSize - steps;\r
2519         triangle[0].y = squareSize;\r
2520         triangle[1].x = squareSize;\r
2521         triangle[1].y = squareSize;\r
2522         triangle[2].x = squareSize;\r
2523         triangle[2].y = squareSize - steps;\r
2524 \r
2525         for( i=0; i<steps; i++ ) {\r
2526             BYTE r = r1 - (r1-r2) * i / steps;\r
2527             BYTE g = g1 - (g1-g2) * i / steps;\r
2528             BYTE b = b1 - (b1-b2) * i / steps;\r
2529 \r
2530             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2531             hbrush_old = SelectObject( hdc, hbrush );\r
2532             Polygon( hdc, triangle, 3 );\r
2533             SelectObject( hdc, hbrush_old );\r
2534             DeleteObject(hbrush);\r
2535             triangle[0].x++;\r
2536             triangle[2].y++;\r
2537         }\r
2538 \r
2539         SelectObject( hdc, hpen );\r
2540     }\r
2541 }\r
2542 \r
2543 /*\r
2544     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2545     seems to work ok. The main problem here is to find the "inside" of a chess\r
2546     piece: follow the steps as explained below.\r
2547 */\r
2548 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2549 {\r
2550     HBITMAP hbm;\r
2551     HBITMAP hbm_old;\r
2552     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2553     RECT rc;\r
2554     SIZE sz;\r
2555     POINT pt;\r
2556     int backColor = whitePieceColor; \r
2557     int foreColor = blackPieceColor;\r
2558     \r
2559     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2560         backColor = appData.fontBackColorWhite;\r
2561         foreColor = appData.fontForeColorWhite;\r
2562     }\r
2563     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2564         backColor = appData.fontBackColorBlack;\r
2565         foreColor = appData.fontForeColorBlack;\r
2566     }\r
2567 \r
2568     /* Mask */\r
2569     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2570 \r
2571     hbm_old = SelectObject( hdc, hbm );\r
2572 \r
2573     rc.left = 0;\r
2574     rc.top = 0;\r
2575     rc.right = squareSize;\r
2576     rc.bottom = squareSize;\r
2577 \r
2578     /* Step 1: background is now black */\r
2579     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2580 \r
2581     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2582 \r
2583     pt.x = (squareSize - sz.cx) / 2;\r
2584     pt.y = (squareSize - sz.cy) / 2;\r
2585 \r
2586     SetBkMode( hdc, TRANSPARENT );\r
2587     SetTextColor( hdc, chroma );\r
2588     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2589     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2590 \r
2591     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2592     /* Step 3: the area outside the piece is filled with white */\r
2593 //    FloodFill( hdc, 0, 0, chroma );\r
2594     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2595     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2596     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2597     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2598     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2599     /* \r
2600         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2601         but if the start point is not inside the piece we're lost!\r
2602         There should be a better way to do this... if we could create a region or path\r
2603         from the fill operation we would be fine for example.\r
2604     */\r
2605 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2606     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2607 \r
2608     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2609         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2610         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2611 \r
2612         SelectObject( dc2, bm2 );\r
2613         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2614         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2615         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2616         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2617         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2618 \r
2619         DeleteDC( dc2 );\r
2620         DeleteObject( bm2 );\r
2621     }\r
2622 \r
2623     SetTextColor( hdc, 0 );\r
2624     /* \r
2625         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2626         draw the piece again in black for safety.\r
2627     */\r
2628     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2629 \r
2630     SelectObject( hdc, hbm_old );\r
2631 \r
2632     if( hPieceMask[index] != NULL ) {\r
2633         DeleteObject( hPieceMask[index] );\r
2634     }\r
2635 \r
2636     hPieceMask[index] = hbm;\r
2637 \r
2638     /* Face */\r
2639     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2640 \r
2641     SelectObject( hdc, hbm );\r
2642 \r
2643     {\r
2644         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2645         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2646         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2647 \r
2648         SelectObject( dc1, hPieceMask[index] );\r
2649         SelectObject( dc2, bm2 );\r
2650         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2651         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2652         \r
2653         /* \r
2654             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2655             the piece background and deletes (makes transparent) the rest.\r
2656             Thanks to that mask, we are free to paint the background with the greates\r
2657             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2658             We use this, to make gradients and give the pieces a "roundish" look.\r
2659         */\r
2660         SetPieceBackground( hdc, backColor, 2 );\r
2661         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2662 \r
2663         DeleteDC( dc2 );\r
2664         DeleteDC( dc1 );\r
2665         DeleteObject( bm2 );\r
2666     }\r
2667 \r
2668     SetTextColor( hdc, foreColor );\r
2669     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2670 \r
2671     SelectObject( hdc, hbm_old );\r
2672 \r
2673     if( hPieceFace[index] != NULL ) {\r
2674         DeleteObject( hPieceFace[index] );\r
2675     }\r
2676 \r
2677     hPieceFace[index] = hbm;\r
2678 }\r
2679 \r
2680 static int TranslatePieceToFontPiece( int piece )\r
2681 {\r
2682     switch( piece ) {\r
2683     case BlackPawn:\r
2684         return PM_BP;\r
2685     case BlackKnight:\r
2686         return PM_BN;\r
2687     case BlackBishop:\r
2688         return PM_BB;\r
2689     case BlackRook:\r
2690         return PM_BR;\r
2691     case BlackQueen:\r
2692         return PM_BQ;\r
2693     case BlackKing:\r
2694         return PM_BK;\r
2695     case WhitePawn:\r
2696         return PM_WP;\r
2697     case WhiteKnight:\r
2698         return PM_WN;\r
2699     case WhiteBishop:\r
2700         return PM_WB;\r
2701     case WhiteRook:\r
2702         return PM_WR;\r
2703     case WhiteQueen:\r
2704         return PM_WQ;\r
2705     case WhiteKing:\r
2706         return PM_WK;\r
2707 \r
2708     case BlackAngel:\r
2709         return PM_BA;\r
2710     case BlackMarshall:\r
2711         return PM_BC;\r
2712     case BlackFerz:\r
2713         return PM_BF;\r
2714     case BlackNightrider:\r
2715         return PM_BH;\r
2716     case BlackAlfil:\r
2717         return PM_BE;\r
2718     case BlackWazir:\r
2719         return PM_BW;\r
2720     case BlackUnicorn:\r
2721         return PM_BU;\r
2722     case BlackCannon:\r
2723         return PM_BO;\r
2724     case BlackGrasshopper:\r
2725         return PM_BG;\r
2726     case BlackMan:\r
2727         return PM_BM;\r
2728     case BlackSilver:\r
2729         return PM_BSG;\r
2730     case BlackLance:\r
2731         return PM_BL;\r
2732     case BlackFalcon:\r
2733         return PM_BV;\r
2734     case BlackCobra:\r
2735         return PM_BS;\r
2736     case BlackCardinal:\r
2737         return PM_BAB;\r
2738     case BlackDragon:\r
2739         return PM_BD;\r
2740 \r
2741     case WhiteAngel:\r
2742         return PM_WA;\r
2743     case WhiteMarshall:\r
2744         return PM_WC;\r
2745     case WhiteFerz:\r
2746         return PM_WF;\r
2747     case WhiteNightrider:\r
2748         return PM_WH;\r
2749     case WhiteAlfil:\r
2750         return PM_WE;\r
2751     case WhiteWazir:\r
2752         return PM_WW;\r
2753     case WhiteUnicorn:\r
2754         return PM_WU;\r
2755     case WhiteCannon:\r
2756         return PM_WO;\r
2757     case WhiteGrasshopper:\r
2758         return PM_WG;\r
2759     case WhiteMan:\r
2760         return PM_WM;\r
2761     case WhiteSilver:\r
2762         return PM_WSG;\r
2763     case WhiteLance:\r
2764         return PM_WL;\r
2765     case WhiteFalcon:\r
2766         return PM_WV;\r
2767     case WhiteCobra:\r
2768         return PM_WS;\r
2769     case WhiteCardinal:\r
2770         return PM_WAB;\r
2771     case WhiteDragon:\r
2772         return PM_WD;\r
2773     }\r
2774 \r
2775     return 0;\r
2776 }\r
2777 \r
2778 void CreatePiecesFromFont()\r
2779 {\r
2780     LOGFONT lf;\r
2781     HDC hdc_window = NULL;\r
2782     HDC hdc = NULL;\r
2783     HFONT hfont_old;\r
2784     int fontHeight;\r
2785     int i;\r
2786 \r
2787     if( fontBitmapSquareSize < 0 ) {\r
2788         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2789         return;\r
2790     }\r
2791 \r
2792     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2793         fontBitmapSquareSize = -1;\r
2794         return;\r
2795     }\r
2796 \r
2797     if( fontBitmapSquareSize != squareSize ) {\r
2798         hdc_window = GetDC( hwndMain );\r
2799         hdc = CreateCompatibleDC( hdc_window );\r
2800 \r
2801         if( hPieceFont != NULL ) {\r
2802             DeleteObject( hPieceFont );\r
2803         }\r
2804         else {\r
2805             for( i=0; i<=(int)BlackKing; i++ ) {\r
2806                 hPieceMask[i] = NULL;\r
2807                 hPieceFace[i] = NULL;\r
2808             }\r
2809         }\r
2810 \r
2811         fontHeight = 75;\r
2812 \r
2813         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2814             fontHeight = appData.fontPieceSize;\r
2815         }\r
2816 \r
2817         fontHeight = (fontHeight * squareSize) / 100;\r
2818 \r
2819         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2820         lf.lfWidth = 0;\r
2821         lf.lfEscapement = 0;\r
2822         lf.lfOrientation = 0;\r
2823         lf.lfWeight = FW_NORMAL;\r
2824         lf.lfItalic = 0;\r
2825         lf.lfUnderline = 0;\r
2826         lf.lfStrikeOut = 0;\r
2827         lf.lfCharSet = DEFAULT_CHARSET;\r
2828         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2829         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2830         lf.lfQuality = PROOF_QUALITY;\r
2831         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2832         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2833         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2834 \r
2835         hPieceFont = CreateFontIndirect( &lf );\r
2836 \r
2837         if( hPieceFont == NULL ) {\r
2838             fontBitmapSquareSize = -2;\r
2839         }\r
2840         else {\r
2841             /* Setup font-to-piece character table */\r
2842             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2843                 /* No (or wrong) global settings, try to detect the font */\r
2844                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2845                     /* Alpha */\r
2846                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2847                 }\r
2848                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2849                     /* DiagramTT* family */\r
2850                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2851                 }\r
2852                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2853                     /* Fairy symbols */\r
2854                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2855                 }\r
2856                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2857                     /* Good Companion (Some characters get warped as literal :-( */\r
2858                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2859                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2860                     SetCharTable(pieceToFontChar, s);\r
2861                 }\r
2862                 else {\r
2863                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2864                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2865                 }\r
2866             }\r
2867 \r
2868             /* Create bitmaps */\r
2869             hfont_old = SelectObject( hdc, hPieceFont );\r
2870 #if 0\r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2875             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2883 \r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2916 #else\r
2917             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2918                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2919                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2920 #endif\r
2921             SelectObject( hdc, hfont_old );\r
2922 \r
2923             fontBitmapSquareSize = squareSize;\r
2924         }\r
2925     }\r
2926 \r
2927     if( hdc != NULL ) {\r
2928         DeleteDC( hdc );\r
2929     }\r
2930 \r
2931     if( hdc_window != NULL ) {\r
2932         ReleaseDC( hwndMain, hdc_window );\r
2933     }\r
2934 }\r
2935 \r
2936 HBITMAP\r
2937 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2938 {\r
2939   char name[128];\r
2940 \r
2941   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2942   if (gameInfo.event &&\r
2943       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2944       strcmp(name, "k80s") == 0) {\r
2945     strcpy(name, "tim");\r
2946   }\r
2947   return LoadBitmap(hinst, name);\r
2948 }\r
2949 \r
2950 \r
2951 /* Insert a color into the program's logical palette\r
2952    structure.  This code assumes the given color is\r
2953    the result of the RGB or PALETTERGB macro, and it\r
2954    knows how those macros work (which is documented).\r
2955 */\r
2956 VOID\r
2957 InsertInPalette(COLORREF color)\r
2958 {\r
2959   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2960 \r
2961   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2962     DisplayFatalError("Too many colors", 0, 1);\r
2963     pLogPal->palNumEntries--;\r
2964     return;\r
2965   }\r
2966 \r
2967   pe->peFlags = (char) 0;\r
2968   pe->peRed = (char) (0xFF & color);\r
2969   pe->peGreen = (char) (0xFF & (color >> 8));\r
2970   pe->peBlue = (char) (0xFF & (color >> 16));\r
2971   return;\r
2972 }\r
2973 \r
2974 \r
2975 VOID\r
2976 InitDrawingColors()\r
2977 {\r
2978   if (pLogPal == NULL) {\r
2979     /* Allocate enough memory for a logical palette with\r
2980      * PALETTESIZE entries and set the size and version fields\r
2981      * of the logical palette structure.\r
2982      */\r
2983     pLogPal = (NPLOGPALETTE)\r
2984       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2985                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2986     pLogPal->palVersion    = 0x300;\r
2987   }\r
2988   pLogPal->palNumEntries = 0;\r
2989 \r
2990   InsertInPalette(lightSquareColor);\r
2991   InsertInPalette(darkSquareColor);\r
2992   InsertInPalette(whitePieceColor);\r
2993   InsertInPalette(blackPieceColor);\r
2994   InsertInPalette(highlightSquareColor);\r
2995   InsertInPalette(premoveHighlightColor);\r
2996 \r
2997   /*  create a logical color palette according the information\r
2998    *  in the LOGPALETTE structure.\r
2999    */\r
3000   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3001 \r
3002   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3003   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3004   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3005   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3006   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3007   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3008 \r
3009   /* [AS] Force rendering of the font-based pieces */\r
3010   if( fontBitmapSquareSize > 0 ) {\r
3011     fontBitmapSquareSize = 0;\r
3012   }\r
3013 }\r
3014 \r
3015 \r
3016 int\r
3017 BoardWidth(int boardSize, int n)\r
3018 { /* [HGM] argument n added to allow different width and height */\r
3019   int lineGap = sizeInfo[boardSize].lineGap;\r
3020 \r
3021   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3022       lineGap = appData.overrideLineGap;\r
3023   }\r
3024 \r
3025   return (n + 1) * lineGap +\r
3026           n * sizeInfo[boardSize].squareSize;\r
3027 }\r
3028 \r
3029 /* Respond to board resize by dragging edge */\r
3030 VOID\r
3031 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3032 {\r
3033   BoardSize newSize = NUM_SIZES - 1;\r
3034   static int recurse = 0;\r
3035   if (IsIconic(hwndMain)) return;\r
3036   if (recurse > 0) return;\r
3037   recurse++;\r
3038   while (newSize > 0) {\r
3039         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3040         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3041            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3042     newSize--;\r
3043   } \r
3044   boardSize = newSize;\r
3045   InitDrawingSizes(boardSize, flags);\r
3046   recurse--;\r
3047 }\r
3048 \r
3049 \r
3050 \r
3051 VOID\r
3052 InitDrawingSizes(BoardSize boardSize, int flags)\r
3053 {\r
3054   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3055   ChessSquare piece;\r
3056   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3057   HDC hdc;\r
3058   SIZE clockSize, messageSize;\r
3059   HFONT oldFont;\r
3060   char buf[MSG_SIZ];\r
3061   char *str;\r
3062   HMENU hmenu = GetMenu(hwndMain);\r
3063   RECT crect, wrect;\r
3064   int offby;\r
3065   LOGBRUSH logbrush;\r
3066 \r
3067   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3068   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3069 \r
3070   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3071   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3072 \r
3073   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3074   smallLayout = sizeInfo[boardSize].smallLayout;\r
3075   squareSize = sizeInfo[boardSize].squareSize;\r
3076   lineGap = sizeInfo[boardSize].lineGap;\r
3077   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3078 \r
3079   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3080       lineGap = appData.overrideLineGap;\r
3081   }\r
3082 \r
3083   if (tinyLayout != oldTinyLayout) {\r
3084     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3085     if (tinyLayout) {\r
3086       style &= ~WS_SYSMENU;\r
3087       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3088                  "&Minimize\tCtrl+F4");\r
3089     } else {\r
3090       style |= WS_SYSMENU;\r
3091       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3092     }\r
3093     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3094 \r
3095     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3096       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3097         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3098     }\r
3099     DrawMenuBar(hwndMain);\r
3100   }\r
3101 \r
3102   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3103   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3104 \r
3105   /* Get text area sizes */\r
3106   hdc = GetDC(hwndMain);\r
3107   if (appData.clockMode) {\r
3108     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3109   } else {\r
3110     sprintf(buf, "White");\r
3111   }\r
3112   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3113   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3114   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3115   str = "We only care about the height here";\r
3116   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3117   SelectObject(hdc, oldFont);\r
3118   ReleaseDC(hwndMain, hdc);\r
3119 \r
3120   /* Compute where everything goes */\r
3121   if(first.programLogo || second.programLogo) {\r
3122         /* [HGM] logo: if either logo is on, reserve space for it */\r
3123         logoHeight =  2*clockSize.cy;\r
3124         leftLogoRect.left   = OUTER_MARGIN;\r
3125         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3126         leftLogoRect.top    = OUTER_MARGIN;\r
3127         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3128 \r
3129         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3130         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3131         rightLogoRect.top    = OUTER_MARGIN;\r
3132         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3133 \r
3134 \r
3135     blackRect.left = leftLogoRect.right;\r
3136     blackRect.right = rightLogoRect.left;\r
3137     blackRect.top = OUTER_MARGIN;\r
3138     blackRect.bottom = blackRect.top + clockSize.cy;\r
3139 \r
3140     whiteRect.left = blackRect.left ;\r
3141     whiteRect.right = blackRect.right;\r
3142     whiteRect.top = blackRect.bottom;\r
3143     whiteRect.bottom = leftLogoRect.bottom;\r
3144   } else {\r
3145     whiteRect.left = OUTER_MARGIN;\r
3146     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3147     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3148     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3149 \r
3150     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3151     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3152     blackRect.top = whiteRect.top;\r
3153     blackRect.bottom = whiteRect.bottom;\r
3154   }\r
3155 \r
3156   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3157   if (appData.showButtonBar) {\r
3158     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3159       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3160   } else {\r
3161     messageRect.right = OUTER_MARGIN + boardWidth;\r
3162   }\r
3163   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3164   messageRect.bottom = messageRect.top + messageSize.cy;\r
3165 \r
3166   boardRect.left = OUTER_MARGIN;\r
3167   boardRect.right = boardRect.left + boardWidth;\r
3168   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3169   boardRect.bottom = boardRect.top + boardHeight;\r
3170 \r
3171   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3172   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3173   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3174   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3175   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3176     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3177   GetWindowRect(hwndMain, &wrect);\r
3178   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3179                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3180   /* compensate if menu bar wrapped */\r
3181   GetClientRect(hwndMain, &crect);\r
3182   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3183   winHeight += offby;\r
3184   switch (flags) {\r
3185   case WMSZ_TOPLEFT:\r
3186     SetWindowPos(hwndMain, NULL, \r
3187                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3188                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3189     break;\r
3190 \r
3191   case WMSZ_TOPRIGHT:\r
3192   case WMSZ_TOP:\r
3193     SetWindowPos(hwndMain, NULL, \r
3194                  wrect.left, wrect.bottom - winHeight, \r
3195                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3196     break;\r
3197 \r
3198   case WMSZ_BOTTOMLEFT:\r
3199   case WMSZ_LEFT:\r
3200     SetWindowPos(hwndMain, NULL, \r
3201                  wrect.right - winWidth, wrect.top, \r
3202                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3203     break;\r
3204 \r
3205   case WMSZ_BOTTOMRIGHT:\r
3206   case WMSZ_BOTTOM:\r
3207   case WMSZ_RIGHT:\r
3208   default:\r
3209     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3210                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3211     break;\r
3212   }\r
3213 \r
3214   hwndPause = NULL;\r
3215   for (i = 0; i < N_BUTTONS; i++) {\r
3216     if (buttonDesc[i].hwnd != NULL) {\r
3217       DestroyWindow(buttonDesc[i].hwnd);\r
3218       buttonDesc[i].hwnd = NULL;\r
3219     }\r
3220     if (appData.showButtonBar) {\r
3221       buttonDesc[i].hwnd =\r
3222         CreateWindow("BUTTON", buttonDesc[i].label,\r
3223                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3224                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3225                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3226                      (HMENU) buttonDesc[i].id,\r
3227                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3228       if (tinyLayout) {\r
3229         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3230                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3231                     MAKELPARAM(FALSE, 0));\r
3232       }\r
3233       if (buttonDesc[i].id == IDM_Pause)\r
3234         hwndPause = buttonDesc[i].hwnd;\r
3235       buttonDesc[i].wndproc = (WNDPROC)\r
3236         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3237     }\r
3238   }\r
3239   if (gridPen != NULL) DeleteObject(gridPen);\r
3240   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3241   if (premovePen != NULL) DeleteObject(premovePen);\r
3242   if (lineGap != 0) {\r
3243     logbrush.lbStyle = BS_SOLID;\r
3244     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3245     gridPen =\r
3246       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3247                    lineGap, &logbrush, 0, NULL);\r
3248     logbrush.lbColor = highlightSquareColor;\r
3249     highlightPen =\r
3250       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3251                    lineGap, &logbrush, 0, NULL);\r
3252 \r
3253     logbrush.lbColor = premoveHighlightColor; \r
3254     premovePen =\r
3255       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3256                    lineGap, &logbrush, 0, NULL);\r
3257 \r
3258     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3259     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3260       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3261       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3262         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3263       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3264         BOARD_WIDTH * (squareSize + lineGap);\r
3265       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3266     }\r
3267     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3268       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3269       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3270         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3271         lineGap / 2 + (i * (squareSize + lineGap));\r
3272       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3273         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3274       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3275     }\r
3276   }\r
3277 \r
3278   /* [HGM] Licensing requirement */\r
3279 #ifdef GOTHIC\r
3280   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3281 #endif\r
3282 #ifdef FALCON\r
3283   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3284 #endif\r
3285   GothicPopUp( "", VariantNormal);\r
3286 \r
3287 \r
3288 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3289   oldBoardSize = boardSize;\r
3290   oldTinyLayout = tinyLayout;\r
3291 \r
3292   /* Load piece bitmaps for this board size */\r
3293   for (i=0; i<=2; i++) {\r
3294     for (piece = WhitePawn;\r
3295          (int) piece < (int) BlackPawn;\r
3296          piece = (ChessSquare) ((int) piece + 1)) {\r
3297       if (pieceBitmap[i][piece] != NULL)\r
3298         DeleteObject(pieceBitmap[i][piece]);\r
3299     }\r
3300   }\r
3301 \r
3302   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3303   // Orthodox Chess pieces\r
3304   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3305   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3306   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3307   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3308   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3309   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3310   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3311   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3312   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3313   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3314   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3315   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3316   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3317   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3318   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3319   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3320     // in Shogi, Hijack the unused Queen for Lance\r
3321     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3322     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3323     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3324   } else {\r
3325     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3326     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3327     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3328   }\r
3329 \r
3330   if(squareSize <= 72 && squareSize >= 33) { \r
3331     /* A & C are available in most sizes now */\r
3332     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3333       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3334       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3335       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3336       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3337       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3338       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3339       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3340       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3341       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3342       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3343       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3344       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3345     } else { // Smirf-like\r
3346       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3347       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3348       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3349     }\r
3350     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3351       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3352       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3353       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3354     } else { // WinBoard standard\r
3355       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3356       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3357       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3358     }\r
3359   }\r
3360 \r
3361 \r
3362   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3363     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3364     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3365     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3366     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3367     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3368     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3369     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3370     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3371     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3372     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3373     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3374     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3375     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3376     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3377     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3378     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3379     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3380     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3381     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3382     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3383     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3384     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3385     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3386     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3387     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3388     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3389     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3390     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3391     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3392     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3393 \r
3394     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3395       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3396       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3397       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3398       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3399       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3400       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3401       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3402       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3403       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3404       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3405       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3406       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3407     } else {\r
3408       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3409       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3410       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3411       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3412       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3413       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3414       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3415       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3416       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3417       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3418       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3419       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3420     }\r
3421 \r
3422   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3423     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3424     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3425     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3426     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3427     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3428     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3429     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3430     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3431     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3432     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3433     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3434     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3435     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3436     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3437   }\r
3438 \r
3439 \r
3440   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3441   /* special Shogi support in this size */\r
3442   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3443       for (piece = WhitePawn;\r
3444            (int) piece < (int) BlackPawn;\r
3445            piece = (ChessSquare) ((int) piece + 1)) {\r
3446         if (pieceBitmap[i][piece] != NULL)\r
3447           DeleteObject(pieceBitmap[i][piece]);\r
3448       }\r
3449     }\r
3450   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3451   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3452   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3453   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3454   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3455   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3456   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3457   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3458   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3459   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3460   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3461   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3462   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3463   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3464   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3465   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3466   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3467   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3468   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3469   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3470   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3471   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3472   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3473   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3474   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3475   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3476   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3477   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3478   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3479   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3480   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3481   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3482   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3484   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3485   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3486   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3487   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3488   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3489   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3490   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3491   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3492   minorSize = 0;\r
3493   }\r
3494 }\r
3495 \r
3496 HBITMAP\r
3497 PieceBitmap(ChessSquare p, int kind)\r
3498 {\r
3499   if ((int) p >= (int) BlackPawn)\r
3500     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3501 \r
3502   return pieceBitmap[kind][(int) p];\r
3503 }\r
3504 \r
3505 /***************************************************************/\r
3506 \r
3507 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3508 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3509 /*\r
3510 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3511 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3512 */\r
3513 \r
3514 VOID\r
3515 SquareToPos(int row, int column, int * x, int * y)\r
3516 {\r
3517   if (flipView) {\r
3518     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3519     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3520   } else {\r
3521     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3522     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3523   }\r
3524 }\r
3525 \r
3526 VOID\r
3527 DrawCoordsOnDC(HDC hdc)\r
3528 {\r
3529   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
3530   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
3531   char str[2] = { NULLCHAR, NULLCHAR };\r
3532   int oldMode, oldAlign, x, y, start, i;\r
3533   HFONT oldFont;\r
3534   HBRUSH oldBrush;\r
3535 \r
3536   if (!appData.showCoords)\r
3537     return;\r
3538 \r
3539   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3540 \r
3541   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3542   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3543   oldAlign = GetTextAlign(hdc);\r
3544   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3545 \r
3546   y = boardRect.top + lineGap;\r
3547   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3548 \r
3549   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3550   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3551     str[0] = files[start + i];\r
3552     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3553     y += squareSize + lineGap;\r
3554   }\r
3555 \r
3556   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3557 \r
3558   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3559   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3560     str[0] = ranks[start + i];\r
3561     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3562     x += squareSize + lineGap;\r
3563   }    \r
3564 \r
3565   SelectObject(hdc, oldBrush);\r
3566   SetBkMode(hdc, oldMode);\r
3567   SetTextAlign(hdc, oldAlign);\r
3568   SelectObject(hdc, oldFont);\r
3569 }\r
3570 \r
3571 VOID\r
3572 DrawGridOnDC(HDC hdc)\r
3573 {\r
3574   HPEN oldPen;\r
3575  \r
3576   if (lineGap != 0) {\r
3577     oldPen = SelectObject(hdc, gridPen);\r
3578     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3579     SelectObject(hdc, oldPen);\r
3580   }\r
3581 }\r
3582 \r
3583 #define HIGHLIGHT_PEN 0\r
3584 #define PREMOVE_PEN   1\r
3585 \r
3586 VOID\r
3587 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3588 {\r
3589   int x1, y1;\r
3590   HPEN oldPen, hPen;\r
3591   if (lineGap == 0) return;\r
3592   if (flipView) {\r
3593     x1 = boardRect.left +\r
3594       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3595     y1 = boardRect.top +\r
3596       lineGap/2 + y * (squareSize + lineGap);\r
3597   } else {\r
3598     x1 = boardRect.left +\r
3599       lineGap/2 + x * (squareSize + lineGap);\r
3600     y1 = boardRect.top +\r
3601       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3602   }\r
3603   hPen = pen ? premovePen : highlightPen;\r
3604   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3605   MoveToEx(hdc, x1, y1, NULL);\r
3606   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3607   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3608   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3609   LineTo(hdc, x1, y1);\r
3610   SelectObject(hdc, oldPen);\r
3611 }\r
3612 \r
3613 VOID\r
3614 DrawHighlightsOnDC(HDC hdc)\r
3615 {\r
3616   int i;\r
3617   for (i=0; i<2; i++) {\r
3618     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3619       DrawHighlightOnDC(hdc, TRUE,\r
3620                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3621                         HIGHLIGHT_PEN);\r
3622   }\r
3623   for (i=0; i<2; i++) {\r
3624     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3625         premoveHighlightInfo.sq[i].y >= 0) {\r
3626         DrawHighlightOnDC(hdc, TRUE,\r
3627                           premoveHighlightInfo.sq[i].x, \r
3628                           premoveHighlightInfo.sq[i].y,\r
3629                           PREMOVE_PEN);\r
3630     }\r
3631   }\r
3632 }\r
3633 \r
3634 /* Note: sqcolor is used only in monoMode */\r
3635 /* Note that this code is largely duplicated in woptions.c,\r
3636    function DrawSampleSquare, so that needs to be updated too */\r
3637 VOID\r
3638 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3639 {\r
3640   HBITMAP oldBitmap;\r
3641   HBRUSH oldBrush;\r
3642   int tmpSize;\r
3643 \r
3644   if (appData.blindfold) return;\r
3645 \r
3646   /* [AS] Use font-based pieces if needed */\r
3647   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3648     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3649     CreatePiecesFromFont();\r
3650 \r
3651     if( fontBitmapSquareSize == squareSize ) {\r
3652         int index = TranslatePieceToFontPiece(piece);\r
3653 \r
3654         SelectObject( tmphdc, hPieceMask[ index ] );\r
3655 \r
3656         BitBlt( hdc,\r
3657             x, y,\r
3658             squareSize, squareSize,\r
3659             tmphdc,\r
3660             0, 0,\r
3661             SRCAND );\r
3662 \r
3663         SelectObject( tmphdc, hPieceFace[ index ] );\r
3664 \r
3665         BitBlt( hdc,\r
3666             x, y,\r
3667             squareSize, squareSize,\r
3668             tmphdc,\r
3669             0, 0,\r
3670             SRCPAINT );\r
3671 \r
3672         return;\r
3673     }\r
3674   }\r
3675 \r
3676   if (appData.monoMode) {\r
3677     SelectObject(tmphdc, PieceBitmap(piece, \r
3678       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3679     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3680            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3681   } else {\r
3682     tmpSize = squareSize;\r
3683     if(minorSize &&\r
3684         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3685          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3686       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3687       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3688       x += (squareSize - minorSize)>>1;\r
3689       y += squareSize - minorSize - 2;\r
3690       tmpSize = minorSize;\r
3691     }\r
3692     if (color || appData.allWhite ) {\r
3693       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3694       if( color )\r
3695               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3696       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3697       if(appData.upsideDown && color==flipView)\r
3698         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3699       else\r
3700         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3701 #if 0\r
3702       /* Use black piece color for outline of white pieces */\r
3703       /* Not sure this looks really good (though xboard does it).\r
3704          Maybe better to have another selectable color, default black */\r
3705       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3706       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3707       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3708 #else\r
3709       /* Use black for outline of white pieces */\r
3710       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3711       if(appData.upsideDown && color==flipView)\r
3712         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3713       else\r
3714         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3715 #endif\r
3716     } else {\r
3717 #if 0\r
3718       /* Use white piece color for details of black pieces */\r
3719       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3720          WHITE_PIECE ones aren't always the right shape. */\r
3721       /* Not sure this looks really good (though xboard does it).\r
3722          Maybe better to have another selectable color, default medium gray? */\r
3723       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3724       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3725       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3726       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3727       SelectObject(hdc, blackPieceBrush);\r
3728       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3729 #else\r
3730       /* Use square color for details of black pieces */\r
3731       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3732       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3733       if(appData.upsideDown && !flipView)\r
3734         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3735       else\r
3736         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3737 #endif\r
3738     }\r
3739     SelectObject(hdc, oldBrush);\r
3740     SelectObject(tmphdc, oldBitmap);\r
3741   }\r
3742 }\r
3743 \r
3744 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3745 int GetBackTextureMode( int algo )\r
3746 {\r
3747     int result = BACK_TEXTURE_MODE_DISABLED;\r
3748 \r
3749     switch( algo ) \r
3750     {\r
3751         case BACK_TEXTURE_MODE_PLAIN:\r
3752             result = 1; /* Always use identity map */\r
3753             break;\r
3754         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3755             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3756             break;\r
3757     }\r
3758 \r
3759     return result;\r
3760 }\r
3761 \r
3762 /* \r
3763     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3764     to handle redraws cleanly (as random numbers would always be different).\r
3765 */\r
3766 VOID RebuildTextureSquareInfo()\r
3767 {\r
3768     BITMAP bi;\r
3769     int lite_w = 0;\r
3770     int lite_h = 0;\r
3771     int dark_w = 0;\r
3772     int dark_h = 0;\r
3773     int row;\r
3774     int col;\r
3775 \r
3776     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3777 \r
3778     if( liteBackTexture != NULL ) {\r
3779         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3780             lite_w = bi.bmWidth;\r
3781             lite_h = bi.bmHeight;\r
3782         }\r
3783     }\r
3784 \r
3785     if( darkBackTexture != NULL ) {\r
3786         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3787             dark_w = bi.bmWidth;\r
3788             dark_h = bi.bmHeight;\r
3789         }\r
3790     }\r
3791 \r
3792     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3793         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3794             if( (col + row) & 1 ) {\r
3795                 /* Lite square */\r
3796                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3797                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3798                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3799                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3800                 }\r
3801             }\r
3802             else {\r
3803                 /* Dark square */\r
3804                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3805                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3806                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3807                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3808                 }\r
3809             }\r
3810         }\r
3811     }\r
3812 }\r
3813 \r
3814 /* [AS] Arrow highlighting support */\r
3815 \r
3816 static int A_WIDTH = 5; /* Width of arrow body */\r
3817 \r
3818 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3819 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3820 \r
3821 static double Sqr( double x )\r
3822 {\r
3823     return x*x;\r
3824 }\r
3825 \r
3826 static int Round( double x )\r
3827 {\r
3828     return (int) (x + 0.5);\r
3829 }\r
3830 \r
3831 /* Draw an arrow between two points using current settings */\r
3832 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3833 {\r
3834     POINT arrow[7];\r
3835     double dx, dy, j, k, x, y;\r
3836 \r
3837     if( d_x == s_x ) {\r
3838         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3839 \r
3840         arrow[0].x = s_x + A_WIDTH;\r
3841         arrow[0].y = s_y;\r
3842 \r
3843         arrow[1].x = s_x + A_WIDTH;\r
3844         arrow[1].y = d_y - h;\r
3845 \r
3846         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3847         arrow[2].y = d_y - h;\r
3848 \r
3849         arrow[3].x = d_x;\r
3850         arrow[3].y = d_y;\r
3851 \r
3852         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3853         arrow[4].y = d_y - h;\r
3854 \r
3855         arrow[5].x = s_x - A_WIDTH;\r
3856         arrow[5].y = d_y - h;\r
3857 \r
3858         arrow[6].x = s_x - A_WIDTH;\r
3859         arrow[6].y = s_y;\r
3860     }\r
3861     else if( d_y == s_y ) {\r
3862         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3863 \r
3864         arrow[0].x = s_x;\r
3865         arrow[0].y = s_y + A_WIDTH;\r
3866 \r
3867         arrow[1].x = d_x - w;\r
3868         arrow[1].y = s_y + A_WIDTH;\r
3869 \r
3870         arrow[2].x = d_x - w;\r
3871         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3872 \r
3873         arrow[3].x = d_x;\r
3874         arrow[3].y = d_y;\r
3875 \r
3876         arrow[4].x = d_x - w;\r
3877         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3878 \r
3879         arrow[5].x = d_x - w;\r
3880         arrow[5].y = s_y - A_WIDTH;\r
3881 \r
3882         arrow[6].x = s_x;\r
3883         arrow[6].y = s_y - A_WIDTH;\r
3884     }\r
3885     else {\r
3886         /* [AS] Needed a lot of paper for this! :-) */\r
3887         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3888         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3889   \r
3890         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3891 \r
3892         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3893 \r
3894         x = s_x;\r
3895         y = s_y;\r
3896 \r
3897         arrow[0].x = Round(x - j);\r
3898         arrow[0].y = Round(y + j*dx);\r
3899 \r
3900         arrow[1].x = Round(x + j);\r
3901         arrow[1].y = Round(y - j*dx);\r
3902 \r
3903         if( d_x > s_x ) {\r
3904             x = (double) d_x - k;\r
3905             y = (double) d_y - k*dy;\r
3906         }\r
3907         else {\r
3908             x = (double) d_x + k;\r
3909             y = (double) d_y + k*dy;\r
3910         }\r
3911 \r
3912         arrow[2].x = Round(x + j);\r
3913         arrow[2].y = Round(y - j*dx);\r
3914 \r
3915         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3916         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3917 \r
3918         arrow[4].x = d_x;\r
3919         arrow[4].y = d_y;\r
3920 \r
3921         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3922         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3923 \r
3924         arrow[6].x = Round(x - j);\r
3925         arrow[6].y = Round(y + j*dx);\r
3926     }\r
3927 \r
3928     Polygon( hdc, arrow, 7 );\r
3929 }\r
3930 \r
3931 /* [AS] Draw an arrow between two squares */\r
3932 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3933 {\r
3934     int s_x, s_y, d_x, d_y;\r
3935     HPEN hpen;\r
3936     HPEN holdpen;\r
3937     HBRUSH hbrush;\r
3938     HBRUSH holdbrush;\r
3939     LOGBRUSH stLB;\r
3940 \r
3941     if( s_col == d_col && s_row == d_row ) {\r
3942         return;\r
3943     }\r
3944 \r
3945     /* Get source and destination points */\r
3946     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3947     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3948 \r
3949     if( d_y > s_y ) {\r
3950         d_y += squareSize / 4;\r
3951     }\r
3952     else if( d_y < s_y ) {\r
3953         d_y += 3 * squareSize / 4;\r
3954     }\r
3955     else {\r
3956         d_y += squareSize / 2;\r
3957     }\r
3958 \r
3959     if( d_x > s_x ) {\r
3960         d_x += squareSize / 4;\r
3961     }\r
3962     else if( d_x < s_x ) {\r
3963         d_x += 3 * squareSize / 4;\r
3964     }\r
3965     else {\r
3966         d_x += squareSize / 2;\r
3967     }\r
3968 \r
3969     s_x += squareSize / 2;\r
3970     s_y += squareSize / 2;\r
3971 \r
3972     /* Adjust width */\r
3973     A_WIDTH = squareSize / 14;\r
3974 \r
3975     /* Draw */\r
3976     stLB.lbStyle = BS_SOLID;\r
3977     stLB.lbColor = appData.highlightArrowColor;\r
3978     stLB.lbHatch = 0;\r
3979 \r
3980     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3981     holdpen = SelectObject( hdc, hpen );\r
3982     hbrush = CreateBrushIndirect( &stLB );\r
3983     holdbrush = SelectObject( hdc, hbrush );\r
3984 \r
3985     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3986 \r
3987     SelectObject( hdc, holdpen );\r
3988     SelectObject( hdc, holdbrush );\r
3989     DeleteObject( hpen );\r
3990     DeleteObject( hbrush );\r
3991 }\r
3992 \r
3993 BOOL HasHighlightInfo()\r
3994 {\r
3995     BOOL result = FALSE;\r
3996 \r
3997     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3998         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3999     {\r
4000         result = TRUE;\r
4001     }\r
4002 \r
4003     return result;\r
4004 }\r
4005 \r
4006 BOOL IsDrawArrowEnabled()\r
4007 {\r
4008     BOOL result = FALSE;\r
4009 \r
4010     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4011         result = TRUE;\r
4012     }\r
4013 \r
4014     return result;\r
4015 }\r
4016 \r
4017 VOID DrawArrowHighlight( HDC hdc )\r
4018 {\r
4019     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4020         DrawArrowBetweenSquares( hdc,\r
4021             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4022             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4023     }\r
4024 }\r
4025 \r
4026 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4027 {\r
4028     HRGN result = NULL;\r
4029 \r
4030     if( HasHighlightInfo() ) {\r
4031         int x1, y1, x2, y2;\r
4032         int sx, sy, dx, dy;\r
4033 \r
4034         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4035         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4036 \r
4037         sx = MIN( x1, x2 );\r
4038         sy = MIN( y1, y2 );\r
4039         dx = MAX( x1, x2 ) + squareSize;\r
4040         dy = MAX( y1, y2 ) + squareSize;\r
4041 \r
4042         result = CreateRectRgn( sx, sy, dx, dy );\r
4043     }\r
4044 \r
4045     return result;\r
4046 }\r
4047 \r
4048 /*\r
4049     Warning: this function modifies the behavior of several other functions. \r
4050     \r
4051     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4052     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4053     repaint is scattered all over the place, which is not good for features such as\r
4054     "arrow highlighting" that require a full repaint of the board.\r
4055 \r
4056     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4057     user interaction, when speed is not so important) but especially to avoid errors\r
4058     in the displayed graphics.\r
4059 \r
4060     In such patched places, I always try refer to this function so there is a single\r
4061     place to maintain knowledge.\r
4062     \r
4063     To restore the original behavior, just return FALSE unconditionally.\r
4064 */\r
4065 BOOL IsFullRepaintPreferrable()\r
4066 {\r
4067     BOOL result = FALSE;\r
4068 \r
4069     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4070         /* Arrow may appear on the board */\r
4071         result = TRUE;\r
4072     }\r
4073 \r
4074     return result;\r
4075 }\r
4076 \r
4077 /* \r
4078     This function is called by DrawPosition to know whether a full repaint must\r
4079     be forced or not.\r
4080 \r
4081     Only DrawPosition may directly call this function, which makes use of \r
4082     some state information. Other function should call DrawPosition specifying \r
4083     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4084 */\r
4085 BOOL DrawPositionNeedsFullRepaint()\r
4086 {\r
4087     BOOL result = FALSE;\r
4088 \r
4089     /* \r
4090         Probably a slightly better policy would be to trigger a full repaint\r
4091         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4092         but animation is fast enough that it's difficult to notice.\r
4093     */\r
4094     if( animInfo.piece == EmptySquare ) {\r
4095         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4096             result = TRUE;\r
4097         }\r
4098     }\r
4099 \r
4100     return result;\r
4101 }\r
4102 \r
4103 VOID\r
4104 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4105 {\r
4106   int row, column, x, y, square_color, piece_color;\r
4107   ChessSquare piece;\r
4108   HBRUSH oldBrush;\r
4109   HDC texture_hdc = NULL;\r
4110 \r
4111   /* [AS] Initialize background textures if needed */\r
4112   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4113       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4114       if( backTextureSquareSize != squareSize \r
4115        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4116           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4117           backTextureSquareSize = squareSize;\r
4118           RebuildTextureSquareInfo();\r
4119       }\r
4120 \r
4121       texture_hdc = CreateCompatibleDC( hdc );\r
4122   }\r
4123 \r
4124   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4125     for (column = 0; column < BOARD_WIDTH; column++) {\r
4126   \r
4127       SquareToPos(row, column, &x, &y);\r
4128 \r
4129       piece = board[row][column];\r
4130 \r
4131       square_color = ((column + row) % 2) == 1;\r
4132       if( gameInfo.variant == VariantXiangqi ) {\r
4133           square_color = !InPalace(row, column);\r
4134           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4135           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4136       }\r
4137       piece_color = (int) piece < (int) BlackPawn;\r
4138 \r
4139 \r
4140       /* [HGM] holdings file: light square or black */\r
4141       if(column == BOARD_LEFT-2) {\r
4142             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4143                 square_color = 1;\r
4144             else {\r
4145                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4146                 continue;\r
4147             }\r
4148       } else\r
4149       if(column == BOARD_RGHT + 1 ) {\r
4150             if( row < gameInfo.holdingsSize )\r
4151                 square_color = 1;\r
4152             else {\r
4153                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4154                 continue;\r
4155             }\r
4156       }\r
4157       if(column == BOARD_LEFT-1 ) /* left align */\r
4158             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4159       else if( column == BOARD_RGHT) /* right align */\r
4160             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4161       else\r
4162       if (appData.monoMode) {\r
4163         if (piece == EmptySquare) {\r
4164           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4165                  square_color ? WHITENESS : BLACKNESS);\r
4166         } else {\r
4167           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4168         }\r
4169       } \r
4170       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4171           /* [AS] Draw the square using a texture bitmap */\r
4172           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4173           int r = row, c = column; // [HGM] do not flip board in flipView\r
4174           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4175 \r
4176           DrawTile( x, y, \r
4177               squareSize, squareSize, \r
4178               hdc, \r
4179               texture_hdc,\r
4180               backTextureSquareInfo[r][c].mode,\r
4181               backTextureSquareInfo[r][c].x,\r
4182               backTextureSquareInfo[r][c].y );\r
4183 \r
4184           SelectObject( texture_hdc, hbm );\r
4185 \r
4186           if (piece != EmptySquare) {\r
4187               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4188           }\r
4189       }\r
4190       else {\r
4191         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4192 \r
4193         oldBrush = SelectObject(hdc, brush );\r
4194         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4195         SelectObject(hdc, oldBrush);\r
4196         if (piece != EmptySquare)\r
4197           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4198       }\r
4199     }\r
4200   }\r
4201 \r
4202   if( texture_hdc != NULL ) {\r
4203     DeleteDC( texture_hdc );\r
4204   }\r
4205 }\r
4206 \r
4207 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4208 void fputDW(FILE *f, int x)\r
4209 {\r
4210         fputc(x     & 255, f);\r
4211         fputc(x>>8  & 255, f);\r
4212         fputc(x>>16 & 255, f);\r
4213         fputc(x>>24 & 255, f);\r
4214 }\r
4215 \r
4216 #define MAX_CLIPS 200   /* more than enough */\r
4217 \r
4218 VOID\r
4219 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4220 {\r
4221 //  HBITMAP bufferBitmap;\r
4222   BITMAP bi;\r
4223 //  RECT Rect;\r
4224   HDC tmphdc;\r
4225   HBITMAP hbm;\r
4226   int w = 100, h = 50;\r
4227 \r
4228   if(cps->programLogo == NULL) return;\r
4229 //  GetClientRect(hwndMain, &Rect);\r
4230 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4231 //                                      Rect.bottom-Rect.top+1);\r
4232   tmphdc = CreateCompatibleDC(hdc);\r
4233   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4234   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4235             w = bi.bmWidth;\r
4236             h = bi.bmHeight;\r
4237   }\r
4238   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4239                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4240   SelectObject(tmphdc, hbm);\r
4241   DeleteDC(tmphdc);\r
4242 }\r
4243 \r
4244 VOID\r
4245 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4246 {\r
4247   static Board lastReq, lastDrawn;\r
4248   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4249   static int lastDrawnFlipView = 0;\r
4250   static int lastReqValid = 0, lastDrawnValid = 0;\r
4251   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4252   HDC tmphdc;\r
4253   HDC hdcmem;\r
4254   HBITMAP bufferBitmap;\r
4255   HBITMAP oldBitmap;\r
4256   RECT Rect;\r
4257   HRGN clips[MAX_CLIPS];\r
4258   ChessSquare dragged_piece = EmptySquare;\r
4259 \r
4260   /* I'm undecided on this - this function figures out whether a full\r
4261    * repaint is necessary on its own, so there's no real reason to have the\r
4262    * caller tell it that.  I think this can safely be set to FALSE - but\r
4263    * if we trust the callers not to request full repaints unnessesarily, then\r
4264    * we could skip some clipping work.  In other words, only request a full\r
4265    * redraw when the majority of pieces have changed positions (ie. flip, \r
4266    * gamestart and similar)  --Hawk\r
4267    */\r
4268   Boolean fullrepaint = repaint;\r
4269 \r
4270   if( DrawPositionNeedsFullRepaint() ) {\r
4271       fullrepaint = TRUE;\r
4272   }\r
4273 \r
4274 #if 0\r
4275   if( fullrepaint ) {\r
4276       static int repaint_count = 0;\r
4277       char buf[128];\r
4278 \r
4279       repaint_count++;\r
4280       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4281       OutputDebugString( buf );\r
4282   }\r
4283 #endif\r
4284 \r
4285   if (board == NULL) {\r
4286     if (!lastReqValid) {\r
4287       return;\r
4288     }\r
4289     board = lastReq;\r
4290   } else {\r
4291     CopyBoard(lastReq, board);\r
4292     lastReqValid = 1;\r
4293   }\r
4294 \r
4295   if (doingSizing) {\r
4296     return;\r
4297   }\r
4298 \r
4299   if (IsIconic(hwndMain)) {\r
4300     return;\r
4301   }\r
4302 \r
4303   if (hdc == NULL) {\r
4304     hdc = GetDC(hwndMain);\r
4305     if (!appData.monoMode) {\r
4306       SelectPalette(hdc, hPal, FALSE);\r
4307       RealizePalette(hdc);\r
4308     }\r
4309     releaseDC = TRUE;\r
4310   } else {\r
4311     releaseDC = FALSE;\r
4312   }\r
4313 \r
4314 #if 0\r
4315   fprintf(debugFP, "*******************************\n"\r
4316                    "repaint = %s\n"\r
4317                    "dragInfo.from (%d,%d)\n"\r
4318                    "dragInfo.start (%d,%d)\n"\r
4319                    "dragInfo.pos (%d,%d)\n"\r
4320                    "dragInfo.lastpos (%d,%d)\n", \r
4321                     repaint ? "TRUE" : "FALSE",\r
4322                     dragInfo.from.x, dragInfo.from.y, \r
4323                     dragInfo.start.x, dragInfo.start.y,\r
4324                     dragInfo.pos.x, dragInfo.pos.y,\r
4325                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4326   fprintf(debugFP, "prev:  ");\r
4327   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4328     for (column = 0; column < BOARD_WIDTH; column++) {\r
4329       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4330     }\r
4331   }\r
4332   fprintf(debugFP, "\n");\r
4333   fprintf(debugFP, "board: ");\r
4334   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4335     for (column = 0; column < BOARD_WIDTH; column++) {\r
4336       fprintf(debugFP, "%d ", board[row][column]);\r
4337     }\r
4338   }\r
4339   fprintf(debugFP, "\n");\r
4340   fflush(debugFP);\r
4341 #endif\r
4342 \r
4343   /* Create some work-DCs */\r
4344   hdcmem = CreateCompatibleDC(hdc);\r
4345   tmphdc = CreateCompatibleDC(hdc);\r
4346 \r
4347   /* If dragging is in progress, we temporarely remove the piece */\r
4348   /* [HGM] or temporarily decrease count if stacked              */\r
4349   /*       !! Moved to before board compare !!                   */\r
4350   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4351     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4352     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4353             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4354         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4355     } else \r
4356     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4357             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4358         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4359     } else \r
4360         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4361   }\r
4362 \r
4363   /* Figure out which squares need updating by comparing the \r
4364    * newest board with the last drawn board and checking if\r
4365    * flipping has changed.\r
4366    */\r
4367   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4368     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4369       for (column = 0; column < BOARD_WIDTH; column++) {\r
4370         if (lastDrawn[row][column] != board[row][column]) {\r
4371           SquareToPos(row, column, &x, &y);\r
4372           clips[num_clips++] =\r
4373             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4374         }\r
4375       }\r
4376     }\r
4377     for (i=0; i<2; i++) {\r
4378       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4379           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4380         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4381             lastDrawnHighlight.sq[i].y >= 0) {\r
4382           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4383                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4384           clips[num_clips++] =\r
4385             CreateRectRgn(x - lineGap, y - lineGap, \r
4386                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4387         }\r
4388         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4389           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4390           clips[num_clips++] =\r
4391             CreateRectRgn(x - lineGap, y - lineGap, \r
4392                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4393         }\r
4394       }\r
4395     }\r
4396     for (i=0; i<2; i++) {\r
4397       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4398           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4399         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4400             lastDrawnPremove.sq[i].y >= 0) {\r
4401           SquareToPos(lastDrawnPremove.sq[i].y,\r
4402                       lastDrawnPremove.sq[i].x, &x, &y);\r
4403           clips[num_clips++] =\r
4404             CreateRectRgn(x - lineGap, y - lineGap, \r
4405                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4406         }\r
4407         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4408             premoveHighlightInfo.sq[i].y >= 0) {\r
4409           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4410                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4411           clips[num_clips++] =\r
4412             CreateRectRgn(x - lineGap, y - lineGap, \r
4413                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4414         }\r
4415       }\r
4416     }\r
4417   } else {\r
4418     fullrepaint = TRUE;\r
4419   }\r
4420 \r
4421   /* Create a buffer bitmap - this is the actual bitmap\r
4422    * being written to.  When all the work is done, we can\r
4423    * copy it to the real DC (the screen).  This avoids\r
4424    * the problems with flickering.\r
4425    */\r
4426   GetClientRect(hwndMain, &Rect);\r
4427   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4428                                         Rect.bottom-Rect.top+1);\r
4429   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4430   if (!appData.monoMode) {\r
4431     SelectPalette(hdcmem, hPal, FALSE);\r
4432   }\r
4433 \r
4434   /* Create clips for dragging */\r
4435   if (!fullrepaint) {\r
4436     if (dragInfo.from.x >= 0) {\r
4437       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4438       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4439     }\r
4440     if (dragInfo.start.x >= 0) {\r
4441       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4442       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4443     }\r
4444     if (dragInfo.pos.x >= 0) {\r
4445       x = dragInfo.pos.x - squareSize / 2;\r
4446       y = dragInfo.pos.y - squareSize / 2;\r
4447       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4448     }\r
4449     if (dragInfo.lastpos.x >= 0) {\r
4450       x = dragInfo.lastpos.x - squareSize / 2;\r
4451       y = dragInfo.lastpos.y - squareSize / 2;\r
4452       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4453     }\r
4454   }\r
4455 \r
4456   /* Are we animating a move?  \r
4457    * If so, \r
4458    *   - remove the piece from the board (temporarely)\r
4459    *   - calculate the clipping region\r
4460    */\r
4461   if (!fullrepaint) {\r
4462     if (animInfo.piece != EmptySquare) {\r
4463       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4464       x = boardRect.left + animInfo.lastpos.x;\r
4465       y = boardRect.top + animInfo.lastpos.y;\r
4466       x2 = boardRect.left + animInfo.pos.x;\r
4467       y2 = boardRect.top + animInfo.pos.y;\r
4468       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4469       /* Slight kludge.  The real problem is that after AnimateMove is\r
4470          done, the position on the screen does not match lastDrawn.\r
4471          This currently causes trouble only on e.p. captures in\r
4472          atomic, where the piece moves to an empty square and then\r
4473          explodes.  The old and new positions both had an empty square\r
4474          at the destination, but animation has drawn a piece there and\r
4475          we have to remember to erase it. */\r
4476       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4477     }\r
4478   }\r
4479 \r
4480   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4481   if (num_clips == 0)\r
4482     fullrepaint = TRUE;\r
4483 \r
4484   /* Set clipping on the memory DC */\r
4485   if (!fullrepaint) {\r
4486     SelectClipRgn(hdcmem, clips[0]);\r
4487     for (x = 1; x < num_clips; x++) {\r
4488       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4489         abort();  // this should never ever happen!\r
4490     }\r
4491   }\r
4492 \r
4493   /* Do all the drawing to the memory DC */\r
4494   DrawGridOnDC(hdcmem);\r
4495   DrawHighlightsOnDC(hdcmem);\r
4496   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4497 \r
4498   if(logoHeight) {\r
4499         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4500         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4501   }\r
4502 \r
4503   if( appData.highlightMoveWithArrow ) {\r
4504     DrawArrowHighlight(hdcmem);\r
4505   }\r
4506 \r
4507   DrawCoordsOnDC(hdcmem);\r
4508 \r
4509   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4510                  /* to make sure lastDrawn contains what is actually drawn */\r
4511 \r
4512   /* Put the dragged piece back into place and draw it (out of place!) */\r
4513     if (dragged_piece != EmptySquare) {\r
4514     /* [HGM] or restack */\r
4515     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4516                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4517     else\r
4518     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4519                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4520     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4521     x = dragInfo.pos.x - squareSize / 2;\r
4522     y = dragInfo.pos.y - squareSize / 2;\r
4523     DrawPieceOnDC(hdcmem, dragged_piece,\r
4524                   ((int) dragged_piece < (int) BlackPawn), \r
4525                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4526   }   \r
4527   \r
4528   /* Put the animated piece back into place and draw it */\r
4529   if (animInfo.piece != EmptySquare) {\r
4530     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4531     x = boardRect.left + animInfo.pos.x;\r
4532     y = boardRect.top + animInfo.pos.y;\r
4533     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4534                   ((int) animInfo.piece < (int) BlackPawn),\r
4535                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4536   }\r
4537 \r
4538   /* Release the bufferBitmap by selecting in the old bitmap \r
4539    * and delete the memory DC\r
4540    */\r
4541   SelectObject(hdcmem, oldBitmap);\r
4542   DeleteDC(hdcmem);\r
4543 \r
4544   /* Set clipping on the target DC */\r
4545   if (!fullrepaint) {\r
4546     SelectClipRgn(hdc, clips[0]);\r
4547     for (x = 1; x < num_clips; x++) {\r
4548       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4549         abort();   // this should never ever happen!\r
4550     } \r
4551   }\r
4552 \r
4553   /* Copy the new bitmap onto the screen in one go.\r
4554    * This way we avoid any flickering\r
4555    */\r
4556   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4557   BitBlt(hdc, boardRect.left, boardRect.top,\r
4558          boardRect.right - boardRect.left,\r
4559          boardRect.bottom - boardRect.top,\r
4560          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4561   if(saveDiagFlag) { \r
4562     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4563     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4564 \r
4565     GetObject(bufferBitmap, sizeof(b), &b);\r
4566     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4567         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4568         bih.biWidth = b.bmWidth;\r
4569         bih.biHeight = b.bmHeight;\r
4570         bih.biPlanes = 1;\r
4571         bih.biBitCount = b.bmBitsPixel;\r
4572         bih.biCompression = 0;\r
4573         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4574         bih.biXPelsPerMeter = 0;\r
4575         bih.biYPelsPerMeter = 0;\r
4576         bih.biClrUsed = 0;\r
4577         bih.biClrImportant = 0;\r
4578 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4579 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4580         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4581 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4582 \r
4583 #if 1\r
4584         wb = b.bmWidthBytes;\r
4585         // count colors\r
4586         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4587                 int k = ((int*) pData)[i];\r
4588                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4589                 if(j >= 16) break;\r
4590                 color[j] = k;\r
4591                 if(j >= nrColors) nrColors = j+1;\r
4592         }\r
4593         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4594                 INT p = 0;\r
4595                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4596                     for(w=0; w<(wb>>2); w+=2) {\r
4597                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4598                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4599                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4600                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4601                         pData[p++] = m | j<<4;\r
4602                     }\r
4603                     while(p&3) pData[p++] = 0;\r
4604                 }\r
4605                 fac = 3;\r
4606                 wb = ((wb+31)>>5)<<2;\r
4607         }\r
4608         // write BITMAPFILEHEADER\r
4609         fprintf(diagFile, "BM");\r
4610         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4611         fputDW(diagFile, 0);\r
4612         fputDW(diagFile, 0x36 + (fac?64:0));\r
4613         // write BITMAPINFOHEADER\r
4614         fputDW(diagFile, 40);\r
4615         fputDW(diagFile, b.bmWidth);\r
4616         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4617         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4618         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4619         fputDW(diagFile, 0);\r
4620         fputDW(diagFile, 0);\r
4621         fputDW(diagFile, 0);\r
4622         fputDW(diagFile, 0);\r
4623         fputDW(diagFile, 0);\r
4624         fputDW(diagFile, 0);\r
4625         // write color table\r
4626         if(fac)\r
4627         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4628         // write bitmap data\r
4629         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4630                 fputc(pData[i], diagFile);\r
4631 #endif\r
4632      }\r
4633   }\r
4634 \r
4635   SelectObject(tmphdc, oldBitmap);\r
4636 \r
4637   /* Massive cleanup */\r
4638   for (x = 0; x < num_clips; x++)\r
4639     DeleteObject(clips[x]);\r
4640 \r
4641   DeleteDC(tmphdc);\r
4642   DeleteObject(bufferBitmap);\r
4643 \r
4644   if (releaseDC) \r
4645     ReleaseDC(hwndMain, hdc);\r
4646   \r
4647   if (lastDrawnFlipView != flipView) {\r
4648     if (flipView)\r
4649       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4650     else\r
4651       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4652   }\r
4653 \r
4654 /*  CopyBoard(lastDrawn, board);*/\r
4655   lastDrawnHighlight = highlightInfo;\r
4656   lastDrawnPremove   = premoveHighlightInfo;\r
4657   lastDrawnFlipView = flipView;\r
4658   lastDrawnValid = 1;\r
4659 }\r
4660 \r
4661 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4662 int\r
4663 SaveDiagram(f)\r
4664      FILE *f;\r
4665 {\r
4666     saveDiagFlag = 1; diagFile = f;\r
4667     HDCDrawPosition(NULL, TRUE, NULL);\r
4668 \r
4669     saveDiagFlag = 0;\r
4670 \r
4671 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4672     \r
4673     fclose(f);\r
4674     return TRUE;\r
4675 }\r
4676 \r
4677 \r
4678 /*---------------------------------------------------------------------------*\\r
4679 | CLIENT PAINT PROCEDURE\r
4680 |   This is the main event-handler for the WM_PAINT message.\r
4681 |\r
4682 \*---------------------------------------------------------------------------*/\r
4683 VOID\r
4684 PaintProc(HWND hwnd)\r
4685 {\r
4686   HDC         hdc;\r
4687   PAINTSTRUCT ps;\r
4688   HFONT       oldFont;\r
4689 \r
4690   if((hdc = BeginPaint(hwnd, &ps))) {\r
4691     if (IsIconic(hwnd)) {\r
4692       DrawIcon(hdc, 2, 2, iconCurrent);\r
4693     } else {\r
4694       if (!appData.monoMode) {\r
4695         SelectPalette(hdc, hPal, FALSE);\r
4696         RealizePalette(hdc);\r
4697       }\r
4698       HDCDrawPosition(hdc, 1, NULL);\r
4699       oldFont =\r
4700         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4701       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4702                  ETO_CLIPPED|ETO_OPAQUE,\r
4703                  &messageRect, messageText, strlen(messageText), NULL);\r
4704       SelectObject(hdc, oldFont);\r
4705       DisplayBothClocks();\r
4706     }\r
4707     EndPaint(hwnd,&ps);\r
4708   }\r
4709 \r
4710   return;\r
4711 }\r
4712 \r
4713 \r
4714 /*\r
4715  * If the user selects on a border boundary, return -1; if off the board,\r
4716  *   return -2.  Otherwise map the event coordinate to the square.\r
4717  * The offset boardRect.left or boardRect.top must already have been\r
4718  *   subtracted from x.\r
4719  */\r
4720 int\r
4721 EventToSquare(int x)\r
4722 {\r
4723   if (x <= 0)\r
4724     return -2;\r
4725   if (x < lineGap)\r
4726     return -1;\r
4727   x -= lineGap;\r
4728   if ((x % (squareSize + lineGap)) >= squareSize)\r
4729     return -1;\r
4730   x /= (squareSize + lineGap);\r
4731   if (x >= BOARD_SIZE)\r
4732     return -2;\r
4733   return x;\r
4734 }\r
4735 \r
4736 typedef struct {\r
4737   char piece;\r
4738   int command;\r
4739   char* name;\r
4740 } DropEnable;\r
4741 \r
4742 DropEnable dropEnables[] = {\r
4743   { 'P', DP_Pawn, "Pawn" },\r
4744   { 'N', DP_Knight, "Knight" },\r
4745   { 'B', DP_Bishop, "Bishop" },\r
4746   { 'R', DP_Rook, "Rook" },\r
4747   { 'Q', DP_Queen, "Queen" },\r
4748 };\r
4749 \r
4750 VOID\r
4751 SetupDropMenu(HMENU hmenu)\r
4752 {\r
4753   int i, count, enable;\r
4754   char *p;\r
4755   extern char white_holding[], black_holding[];\r
4756   char item[MSG_SIZ];\r
4757 \r
4758   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4759     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4760                dropEnables[i].piece);\r
4761     count = 0;\r
4762     while (p && *p++ == dropEnables[i].piece) count++;\r
4763     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4764     enable = count > 0 || !appData.testLegality\r
4765       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4766                       && !appData.icsActive);\r
4767     ModifyMenu(hmenu, dropEnables[i].command,\r
4768                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4769                dropEnables[i].command, item);\r
4770   }\r
4771 }\r
4772 \r
4773 static int fromX = -1, fromY = -1, toX, toY;\r
4774 \r
4775 /* Event handler for mouse messages */\r
4776 VOID\r
4777 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4778 {\r
4779   int x, y;\r
4780   POINT pt;\r
4781   static int recursive = 0;\r
4782   HMENU hmenu;\r
4783 //  BOOLEAN needsRedraw = FALSE;\r
4784   BOOLEAN saveAnimate;\r
4785   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4786   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4787   ChessMove moveType;\r
4788 \r
4789   if (recursive) {\r
4790     if (message == WM_MBUTTONUP) {\r
4791       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4792          to the middle button: we simulate pressing the left button too!\r
4793          */\r
4794       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4795       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4796     }\r
4797     return;\r
4798   }\r
4799   recursive++;\r
4800   \r
4801   pt.x = LOWORD(lParam);\r
4802   pt.y = HIWORD(lParam);\r
4803   x = EventToSquare(pt.x - boardRect.left);\r
4804   y = EventToSquare(pt.y - boardRect.top);\r
4805   if (!flipView && y >= 0) {\r
4806     y = BOARD_HEIGHT - 1 - y;\r
4807   }\r
4808   if (flipView && x >= 0) {\r
4809     x = BOARD_WIDTH - 1 - x;\r
4810   }\r
4811 \r
4812   switch (message) {\r
4813   case WM_LBUTTONDOWN:\r
4814     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4815         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4816         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4817         if(gameInfo.holdingsWidth && \r
4818                 (WhiteOnMove(currentMove) \r
4819                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4820                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4821             // click in right holdings, for determining promotion piece\r
4822             ChessSquare p = boards[currentMove][y][x];\r
4823             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4824             if(p != EmptySquare) {\r
4825                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4826                 fromX = fromY = -1;\r
4827                 break;\r
4828             }\r
4829         }\r
4830         DrawPosition(FALSE, boards[currentMove]);\r
4831         break;\r
4832     }\r
4833     ErrorPopDown();\r
4834     sameAgain = FALSE;\r
4835     if (y == -2) {\r
4836       /* Downclick vertically off board; check if on clock */\r
4837       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4838         if (gameMode == EditPosition) {\r
4839           SetWhiteToPlayEvent();\r
4840         } else if (gameMode == IcsPlayingBlack ||\r
4841                    gameMode == MachinePlaysWhite) {\r
4842           CallFlagEvent();\r
4843         } else if (gameMode == EditGame) {\r
4844           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4845         }\r
4846       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4847         if (gameMode == EditPosition) {\r
4848           SetBlackToPlayEvent();\r
4849         } else if (gameMode == IcsPlayingWhite ||\r
4850                    gameMode == MachinePlaysBlack) {\r
4851           CallFlagEvent();\r
4852         } else if (gameMode == EditGame) {\r
4853           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4854         }\r
4855       }\r
4856       if (!appData.highlightLastMove) {\r
4857         ClearHighlights();\r
4858         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4859       }\r
4860       fromX = fromY = -1;\r
4861       dragInfo.start.x = dragInfo.start.y = -1;\r
4862       dragInfo.from = dragInfo.start;\r
4863       break;\r
4864     } else if (x < 0 || y < 0\r
4865       /* [HGM] block clicks between board and holdings */\r
4866               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4867               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4868               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4869         /* EditPosition, empty square, or different color piece;\r
4870            click-click move is possible */\r
4871                                ) {\r
4872       break;\r
4873     } else if (fromX == x && fromY == y) {\r
4874       /* Downclick on same square again */\r
4875       ClearHighlights();\r
4876       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4877       sameAgain = TRUE;  \r
4878     } else if (fromX != -1 &&\r
4879                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4880                                                                         ) {\r
4881       /* Downclick on different square. */\r
4882       /* [HGM] if on holdings file, should count as new first click ! */\r
4883       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4884         toX = x;\r
4885         toY = y;\r
4886         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4887            to make sure move is legal before showing promotion popup */\r
4888         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4889         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4890                 fromX = fromY = -1; \r
4891                 ClearHighlights();\r
4892                 DrawPosition(FALSE, boards[currentMove]);\r
4893                 break; \r
4894         } else \r
4895         if(moveType != ImpossibleMove) {\r
4896           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4897           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4898             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4899               appData.alwaysPromoteToQueen)) {\r
4900                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4901                   if (!appData.highlightLastMove) {\r
4902                       ClearHighlights();\r
4903                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4904                   }\r
4905           } else\r
4906           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4907                   SetHighlights(fromX, fromY, toX, toY);\r
4908                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4909                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4910                      If promotion to Q is legal, all are legal! */\r
4911                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4912                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4913                     // kludge to temporarily execute move on display, wthout promotng yet\r
4914                     promotionChoice = TRUE;\r
4915                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4916                     boards[currentMove][toY][toX] = p;\r
4917                     DrawPosition(FALSE, boards[currentMove]);\r
4918                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4919                     boards[currentMove][toY][toX] = q;\r
4920                   } else\r
4921                   PromotionPopup(hwnd);\r
4922           } else {       /* not a promotion */\r
4923              if (appData.animate || appData.highlightLastMove) {\r
4924                  SetHighlights(fromX, fromY, toX, toY);\r
4925              } else {\r
4926                  ClearHighlights();\r
4927              }\r
4928              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4929              fromX = fromY = -1;\r
4930              if (appData.animate && !appData.highlightLastMove) {\r
4931                   ClearHighlights();\r
4932                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4933              }\r
4934           }\r
4935           break;\r
4936         }\r
4937         if (gotPremove) {\r
4938             /* [HGM] it seemed that braces were missing here */\r
4939             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4940             fromX = fromY = -1;\r
4941             break;\r
4942         }\r
4943       }\r
4944       ClearHighlights();\r
4945       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4946     }\r
4947     /* First downclick, or restart on a square with same color piece */\r
4948     if (!frozen && OKToStartUserMove(x, y)) {\r
4949       fromX = x;\r
4950       fromY = y;\r
4951       dragInfo.lastpos = pt;\r
4952       dragInfo.from.x = fromX;\r
4953       dragInfo.from.y = fromY;\r
4954       dragInfo.start = dragInfo.from;\r
4955       SetCapture(hwndMain);\r
4956     } else {\r
4957       fromX = fromY = -1;\r
4958       dragInfo.start.x = dragInfo.start.y = -1;\r
4959       dragInfo.from = dragInfo.start;\r
4960       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4961     }\r
4962     break;\r
4963 \r
4964   case WM_LBUTTONUP:\r
4965     ReleaseCapture();\r
4966     if (fromX == -1) break;\r
4967     if (x == fromX && y == fromY) {\r
4968       dragInfo.from.x = dragInfo.from.y = -1;\r
4969       /* Upclick on same square */\r
4970       if (sameAgain) {\r
4971         /* Clicked same square twice: abort click-click move */\r
4972         fromX = fromY = -1;\r
4973         gotPremove = 0;\r
4974         ClearPremoveHighlights();\r
4975       } else {\r
4976         /* First square clicked: start click-click move */\r
4977         SetHighlights(fromX, fromY, -1, -1);\r
4978       }\r
4979       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4980     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4981       /* Errant click; ignore */\r
4982       break;\r
4983     } else {\r
4984       /* Finish drag move. */\r
4985     if (appData.debugMode) {\r
4986         fprintf(debugFP, "release\n");\r
4987     }\r
4988       dragInfo.from.x = dragInfo.from.y = -1;\r
4989       toX = x;\r
4990       toY = y;\r
4991       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4992       appData.animate = appData.animate && !appData.animateDragging;\r
4993       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4994       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4995                 fromX = fromY = -1; \r
4996                 ClearHighlights();\r
4997                 DrawPosition(FALSE, boards[currentMove]);\r
4998                 break; \r
4999       } else \r
5000       if(moveType != ImpossibleMove) {\r
5001           /* [HGM] use move type to determine if move is promotion.\r
5002              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5003           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5004             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5005               appData.alwaysPromoteToQueen)) \r
5006                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5007           else \r
5008           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5009                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5010                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5011                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5012                     // kludge to temporarily execute move on display, wthout promotng yet\r
5013                     promotionChoice = TRUE;\r
5014                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5015                     boards[currentMove][toY][toX] = p;\r
5016                     DrawPosition(FALSE, boards[currentMove]);\r
5017                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5018                     boards[currentMove][toY][toX] = q;\r
5019                     break;\r
5020                   } else\r
5021                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5022         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5023       }\r
5024       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5025       appData.animate = saveAnimate;\r
5026       fromX = fromY = -1;\r
5027       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5028         ClearHighlights();\r
5029       }\r
5030       if (appData.animate || appData.animateDragging ||\r
5031           appData.highlightDragging || gotPremove) {\r
5032         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5033       }\r
5034     }\r
5035     dragInfo.start.x = dragInfo.start.y = -1; \r
5036     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5037     break;\r
5038 \r
5039   case WM_MOUSEMOVE:\r
5040     if ((appData.animateDragging || appData.highlightDragging)\r
5041         && (wParam & MK_LBUTTON)\r
5042         && dragInfo.from.x >= 0) \r
5043     {\r
5044       BOOL full_repaint = FALSE;\r
5045 \r
5046       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5047       if (appData.animateDragging) {\r
5048         dragInfo.pos = pt;\r
5049       }\r
5050       if (appData.highlightDragging) {\r
5051         SetHighlights(fromX, fromY, x, y);\r
5052         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5053             full_repaint = TRUE;\r
5054         }\r
5055       }\r
5056       \r
5057       DrawPosition( full_repaint, NULL);\r
5058       \r
5059       dragInfo.lastpos = dragInfo.pos;\r
5060     }\r
5061     break;\r
5062 \r
5063   case WM_MOUSEWHEEL: // [DM]\r
5064     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5065        /* Mouse Wheel is being rolled forward\r
5066         * Play moves forward\r
5067         */\r
5068        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5069                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5070        /* Mouse Wheel is being rolled backward\r
5071         * Play moves backward\r
5072         */\r
5073        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5074                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5075     }\r
5076     break;\r
5077 \r
5078   case WM_MBUTTONDOWN:\r
5079   case WM_RBUTTONDOWN:\r
5080     ErrorPopDown();\r
5081     ReleaseCapture();\r
5082     fromX = fromY = -1;\r
5083     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5084     dragInfo.start.x = dragInfo.start.y = -1;\r
5085     dragInfo.from = dragInfo.start;\r
5086     dragInfo.lastpos = dragInfo.pos;\r
5087     if (appData.highlightDragging) {\r
5088       ClearHighlights();\r
5089     }\r
5090     if(y == -2) {\r
5091       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5092       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5093           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5094       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5095           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5096       }\r
5097     }\r
5098     DrawPosition(TRUE, NULL);\r
5099 \r
5100     switch (gameMode) {\r
5101     case EditPosition:\r
5102     case IcsExamining:\r
5103       if (x < 0 || y < 0) break;\r
5104       fromX = x;\r
5105       fromY = y;\r
5106       if (message == WM_MBUTTONDOWN) {\r
5107         buttonCount = 3;  /* even if system didn't think so */\r
5108         if (wParam & MK_SHIFT) \r
5109           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5110         else\r
5111           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5112       } else { /* message == WM_RBUTTONDOWN */\r
5113 #if 0\r
5114         if (buttonCount == 3) {\r
5115           if (wParam & MK_SHIFT) \r
5116             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5117           else\r
5118             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5119         } else {\r
5120           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5121         }\r
5122 #else\r
5123         /* Just have one menu, on the right button.  Windows users don't\r
5124            think to try the middle one, and sometimes other software steals\r
5125            it, or it doesn't really exist. */\r
5126         if(gameInfo.variant != VariantShogi)\r
5127             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5128         else\r
5129             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5130 #endif\r
5131       }\r
5132       break;\r
5133     case IcsPlayingWhite:\r
5134     case IcsPlayingBlack:\r
5135     case EditGame:\r
5136     case MachinePlaysWhite:\r
5137     case MachinePlaysBlack:\r
5138       if (appData.testLegality &&\r
5139           gameInfo.variant != VariantBughouse &&\r
5140           gameInfo.variant != VariantCrazyhouse) break;\r
5141       if (x < 0 || y < 0) break;\r
5142       fromX = x;\r
5143       fromY = y;\r
5144       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5145       SetupDropMenu(hmenu);\r
5146       MenuPopup(hwnd, pt, hmenu, -1);\r
5147       break;\r
5148     default:\r
5149       break;\r
5150     }\r
5151     break;\r
5152   }\r
5153 \r
5154   recursive--;\r
5155 }\r
5156 \r
5157 /* Preprocess messages for buttons in main window */\r
5158 LRESULT CALLBACK\r
5159 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5160 {\r
5161   int id = GetWindowLong(hwnd, GWL_ID);\r
5162   int i, dir;\r
5163 \r
5164   for (i=0; i<N_BUTTONS; i++) {\r
5165     if (buttonDesc[i].id == id) break;\r
5166   }\r
5167   if (i == N_BUTTONS) return 0;\r
5168   switch (message) {\r
5169   case WM_KEYDOWN:\r
5170     switch (wParam) {\r
5171     case VK_LEFT:\r
5172     case VK_RIGHT:\r
5173       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5174       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5175       return TRUE;\r
5176     }\r
5177     break;\r
5178   case WM_CHAR:\r
5179     switch (wParam) {\r
5180     case '\r':\r
5181       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5182       return TRUE;\r
5183     case '\t':\r
5184       if (appData.icsActive) {\r
5185         if (GetKeyState(VK_SHIFT) < 0) {\r
5186           /* shifted */\r
5187           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5188           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5189           SetFocus(h);\r
5190         } else {\r
5191           /* unshifted */\r
5192           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5193           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5194           SetFocus(h);\r
5195         }\r
5196         return TRUE;\r
5197       }\r
5198       break;\r
5199     default:\r
5200       if (appData.icsActive) {\r
5201         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5202         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5203         SetFocus(h);\r
5204         SendMessage(h, WM_CHAR, wParam, lParam);\r
5205         return TRUE;\r
5206       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5207         PopUpMoveDialog((char)wParam);\r
5208       }\r
5209       break;\r
5210     }\r
5211     break;\r
5212   }\r
5213   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5214 }\r
5215 \r
5216 /* Process messages for Promotion dialog box */\r
5217 LRESULT CALLBACK\r
5218 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5219 {\r
5220   char promoChar;\r
5221 \r
5222   switch (message) {\r
5223   case WM_INITDIALOG: /* message: initialize dialog box */\r
5224     /* Center the dialog over the application window */\r
5225     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5226     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5227       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5228        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5229                SW_SHOW : SW_HIDE);\r
5230     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5231     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5232        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5233          PieceToChar(WhiteAngel) != '~') ||\r
5234         (PieceToChar(BlackAngel) >= 'A' &&\r
5235          PieceToChar(BlackAngel) != '~')   ) ?\r
5236                SW_SHOW : SW_HIDE);\r
5237     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5238        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5239          PieceToChar(WhiteMarshall) != '~') ||\r
5240         (PieceToChar(BlackMarshall) >= 'A' &&\r
5241          PieceToChar(BlackMarshall) != '~')   ) ?\r
5242                SW_SHOW : SW_HIDE);\r
5243     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5244     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5245        gameInfo.variant != VariantShogi ?\r
5246                SW_SHOW : SW_HIDE);\r
5247     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5248        gameInfo.variant != VariantShogi ?\r
5249                SW_SHOW : SW_HIDE);\r
5250     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5251        gameInfo.variant == VariantShogi ?\r
5252                SW_SHOW : SW_HIDE);\r
5253     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5254        gameInfo.variant == VariantShogi ?\r
5255                SW_SHOW : SW_HIDE);\r
5256     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5257        gameInfo.variant == VariantSuper ?\r
5258                SW_SHOW : SW_HIDE);\r
5259     return TRUE;\r
5260 \r
5261   case WM_COMMAND: /* message: received a command */\r
5262     switch (LOWORD(wParam)) {\r
5263     case IDCANCEL:\r
5264       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5265       ClearHighlights();\r
5266       DrawPosition(FALSE, NULL);\r
5267       return TRUE;\r
5268     case PB_King:\r
5269       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5270       break;\r
5271     case PB_Queen:\r
5272       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5273       break;\r
5274     case PB_Rook:\r
5275       promoChar = PieceToChar(BlackRook);\r
5276       break;\r
5277     case PB_Bishop:\r
5278       promoChar = PieceToChar(BlackBishop);\r
5279       break;\r
5280     case PB_Chancellor:\r
5281       promoChar = PieceToChar(BlackMarshall);\r
5282       break;\r
5283     case PB_Archbishop:\r
5284       promoChar = PieceToChar(BlackAngel);\r
5285       break;\r
5286     case PB_Knight:\r
5287       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5288       break;\r
5289     default:\r
5290       return FALSE;\r
5291     }\r
5292     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5293     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5294        only show the popup when we are already sure the move is valid or\r
5295        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5296        will figure out it is a promotion from the promoChar. */\r
5297     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5298     if (!appData.highlightLastMove) {\r
5299       ClearHighlights();\r
5300       DrawPosition(FALSE, NULL);\r
5301     }\r
5302     return TRUE;\r
5303   }\r
5304   return FALSE;\r
5305 }\r
5306 \r
5307 /* Pop up promotion dialog */\r
5308 VOID\r
5309 PromotionPopup(HWND hwnd)\r
5310 {\r
5311   FARPROC lpProc;\r
5312 \r
5313   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5314   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5315     hwnd, (DLGPROC)lpProc);\r
5316   FreeProcInstance(lpProc);\r
5317 }\r
5318 \r
5319 /* Toggle ShowThinking */\r
5320 VOID\r
5321 ToggleShowThinking()\r
5322 {\r
5323   appData.showThinking = !appData.showThinking;\r
5324   ShowThinkingEvent();\r
5325 }\r
5326 \r
5327 VOID\r
5328 LoadGameDialog(HWND hwnd, char* title)\r
5329 {\r
5330   UINT number = 0;\r
5331   FILE *f;\r
5332   char fileTitle[MSG_SIZ];\r
5333   f = OpenFileDialog(hwnd, "rb", "",\r
5334                      appData.oldSaveStyle ? "gam" : "pgn",\r
5335                      GAME_FILT,\r
5336                      title, &number, fileTitle, NULL);\r
5337   if (f != NULL) {\r
5338     cmailMsgLoaded = FALSE;\r
5339     if (number == 0) {\r
5340       int error = GameListBuild(f);\r
5341       if (error) {\r
5342         DisplayError("Cannot build game list", error);\r
5343       } else if (!ListEmpty(&gameList) &&\r
5344                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5345         GameListPopUp(f, fileTitle);\r
5346         return;\r
5347       }\r
5348       GameListDestroy();\r
5349       number = 1;\r
5350     }\r
5351     LoadGame(f, number, fileTitle, FALSE);\r
5352   }\r
5353 }\r
5354 \r
5355 VOID\r
5356 ChangedConsoleFont()\r
5357 {\r
5358   CHARFORMAT cfmt;\r
5359   CHARRANGE tmpsel, sel;\r
5360   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5361   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5362   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5363   PARAFORMAT paraf;\r
5364 \r
5365   cfmt.cbSize = sizeof(CHARFORMAT);\r
5366   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5367   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5368   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5369    * size.  This was undocumented in the version of MSVC++ that I had\r
5370    * when I wrote the code, but is apparently documented now.\r
5371    */\r
5372   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5373   cfmt.bCharSet = f->lf.lfCharSet;\r
5374   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5375   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5376   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5377   /* Why are the following seemingly needed too? */\r
5378   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5379   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5380   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5381   tmpsel.cpMin = 0;\r
5382   tmpsel.cpMax = -1; /*999999?*/\r
5383   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5384   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5385   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5386    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5387    */\r
5388   paraf.cbSize = sizeof(paraf);\r
5389   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5390   paraf.dxStartIndent = 0;\r
5391   paraf.dxOffset = WRAP_INDENT;\r
5392   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5393   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5394 }\r
5395 \r
5396 /*---------------------------------------------------------------------------*\\r
5397  *\r
5398  * Window Proc for main window\r
5399  *\r
5400 \*---------------------------------------------------------------------------*/\r
5401 \r
5402 /* Process messages for main window, etc. */\r
5403 LRESULT CALLBACK\r
5404 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5405 {\r
5406   FARPROC lpProc;\r
5407   int wmId, wmEvent;\r
5408   char *defName;\r
5409   FILE *f;\r
5410   UINT number;\r
5411   char fileTitle[MSG_SIZ];\r
5412   char buf[MSG_SIZ];\r
5413   static SnapData sd;\r
5414 \r
5415   switch (message) {\r
5416 \r
5417   case WM_PAINT: /* message: repaint portion of window */\r
5418     PaintProc(hwnd);\r
5419     break;\r
5420 \r
5421   case WM_ERASEBKGND:\r
5422     if (IsIconic(hwnd)) {\r
5423       /* Cheat; change the message */\r
5424       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5425     } else {\r
5426       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5427     }\r
5428     break;\r
5429 \r
5430   case WM_LBUTTONDOWN:\r
5431   case WM_MBUTTONDOWN:\r
5432   case WM_RBUTTONDOWN:\r
5433   case WM_LBUTTONUP:\r
5434   case WM_MBUTTONUP:\r
5435   case WM_RBUTTONUP:\r
5436   case WM_MOUSEMOVE:\r
5437   case WM_MOUSEWHEEL:\r
5438     MouseEvent(hwnd, message, wParam, lParam);\r
5439     break;\r
5440 \r
5441   case WM_CHAR:\r
5442     \r
5443     if (appData.icsActive) {\r
5444       if (wParam == '\t') {\r
5445         if (GetKeyState(VK_SHIFT) < 0) {\r
5446           /* shifted */\r
5447           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5448           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5449           SetFocus(h);\r
5450         } else {\r
5451           /* unshifted */\r
5452           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5453           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5454           SetFocus(h);\r
5455         }\r
5456       } else {\r
5457         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5458         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5459         SetFocus(h);\r
5460         SendMessage(h, message, wParam, lParam);\r
5461       }\r
5462     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5463       PopUpMoveDialog((char)wParam);\r
5464     }\r
5465     break;\r
5466 \r
5467   case WM_PALETTECHANGED:\r
5468     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5469       int nnew;\r
5470       HDC hdc = GetDC(hwndMain);\r
5471       SelectPalette(hdc, hPal, TRUE);\r
5472       nnew = RealizePalette(hdc);\r
5473       if (nnew > 0) {\r
5474         paletteChanged = TRUE;\r
5475 #if 0\r
5476         UpdateColors(hdc);\r
5477 #else\r
5478         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5479 #endif\r
5480       }\r
5481       ReleaseDC(hwnd, hdc);\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_QUERYNEWPALETTE:\r
5486     if (!appData.monoMode /*&& paletteChanged*/) {\r
5487       int nnew;\r
5488       HDC hdc = GetDC(hwndMain);\r
5489       paletteChanged = FALSE;\r
5490       SelectPalette(hdc, hPal, FALSE);\r
5491       nnew = RealizePalette(hdc);\r
5492       if (nnew > 0) {\r
5493         InvalidateRect(hwnd, &boardRect, FALSE);\r
5494       }\r
5495       ReleaseDC(hwnd, hdc);\r
5496       return TRUE;\r
5497     }\r
5498     return FALSE;\r
5499 \r
5500   case WM_COMMAND: /* message: command from application menu */\r
5501     wmId    = LOWORD(wParam);\r
5502     wmEvent = HIWORD(wParam);\r
5503 \r
5504     switch (wmId) {\r
5505     case IDM_NewGame:\r
5506       ResetGameEvent();\r
5507       AnalysisPopDown();\r
5508       break;\r
5509 \r
5510     case IDM_NewGameFRC:\r
5511       if( NewGameFRC() == 0 ) {\r
5512         ResetGameEvent();\r
5513         AnalysisPopDown();\r
5514       }\r
5515       break;\r
5516 \r
5517     case IDM_NewVariant:\r
5518       NewVariantPopup(hwnd);\r
5519       break;\r
5520 \r
5521     case IDM_LoadGame:\r
5522       LoadGameDialog(hwnd, "Load Game from File");\r
5523       break;\r
5524 \r
5525     case IDM_LoadNextGame:\r
5526       ReloadGame(1);\r
5527       break;\r
5528 \r
5529     case IDM_LoadPrevGame:\r
5530       ReloadGame(-1);\r
5531       break;\r
5532 \r
5533     case IDM_ReloadGame:\r
5534       ReloadGame(0);\r
5535       break;\r
5536 \r
5537     case IDM_LoadPosition:\r
5538       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5539         Reset(FALSE, TRUE);\r
5540       }\r
5541       number = 1;\r
5542       f = OpenFileDialog(hwnd, "rb", "",\r
5543                          appData.oldSaveStyle ? "pos" : "fen",\r
5544                          POSITION_FILT,\r
5545                          "Load Position from File", &number, fileTitle, NULL);\r
5546       if (f != NULL) {\r
5547         LoadPosition(f, number, fileTitle);\r
5548       }\r
5549       break;\r
5550 \r
5551     case IDM_LoadNextPosition:\r
5552       ReloadPosition(1);\r
5553       break;\r
5554 \r
5555     case IDM_LoadPrevPosition:\r
5556       ReloadPosition(-1);\r
5557       break;\r
5558 \r
5559     case IDM_ReloadPosition:\r
5560       ReloadPosition(0);\r
5561       break;\r
5562 \r
5563     case IDM_SaveGame:\r
5564       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5565       f = OpenFileDialog(hwnd, "a", defName,\r
5566                          appData.oldSaveStyle ? "gam" : "pgn",\r
5567                          GAME_FILT,\r
5568                          "Save Game to File", NULL, fileTitle, NULL);\r
5569       if (f != NULL) {\r
5570         SaveGame(f, 0, "");\r
5571       }\r
5572       break;\r
5573 \r
5574     case IDM_SavePosition:\r
5575       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5576       f = OpenFileDialog(hwnd, "a", defName,\r
5577                          appData.oldSaveStyle ? "pos" : "fen",\r
5578                          POSITION_FILT,\r
5579                          "Save Position to File", NULL, fileTitle, NULL);\r
5580       if (f != NULL) {\r
5581         SavePosition(f, 0, "");\r
5582       }\r
5583       break;\r
5584 \r
5585     case IDM_SaveDiagram:\r
5586       defName = "diagram";\r
5587       f = OpenFileDialog(hwnd, "wb", defName,\r
5588                          "bmp",\r
5589                          DIAGRAM_FILT,\r
5590                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5591       if (f != NULL) {\r
5592         SaveDiagram(f);\r
5593       }\r
5594       break;\r
5595 \r
5596     case IDM_CopyGame:\r
5597       CopyGameToClipboard();\r
5598       break;\r
5599 \r
5600     case IDM_PasteGame:\r
5601       PasteGameFromClipboard();\r
5602       break;\r
5603 \r
5604     case IDM_CopyGameListToClipboard:\r
5605       CopyGameListToClipboard();\r
5606       break;\r
5607 \r
5608     /* [AS] Autodetect FEN or PGN data */\r
5609     case IDM_PasteAny:\r
5610       PasteGameOrFENFromClipboard();\r
5611       break;\r
5612 \r
5613     /* [AS] Move history */\r
5614     case IDM_ShowMoveHistory:\r
5615         if( MoveHistoryIsUp() ) {\r
5616             MoveHistoryPopDown();\r
5617         }\r
5618         else {\r
5619             MoveHistoryPopUp();\r
5620         }\r
5621         break;\r
5622 \r
5623     /* [AS] Eval graph */\r
5624     case IDM_ShowEvalGraph:\r
5625         if( EvalGraphIsUp() ) {\r
5626             EvalGraphPopDown();\r
5627         }\r
5628         else {\r
5629             EvalGraphPopUp();\r
5630         }\r
5631         break;\r
5632 \r
5633     /* [AS] Engine output */\r
5634     case IDM_ShowEngineOutput:\r
5635         if( EngineOutputIsUp() ) {\r
5636             EngineOutputPopDown();\r
5637         }\r
5638         else {\r
5639             EngineOutputPopUp();\r
5640         }\r
5641         break;\r
5642 \r
5643     /* [AS] User adjudication */\r
5644     case IDM_UserAdjudication_White:\r
5645         UserAdjudicationEvent( +1 );\r
5646         break;\r
5647 \r
5648     case IDM_UserAdjudication_Black:\r
5649         UserAdjudicationEvent( -1 );\r
5650         break;\r
5651 \r
5652     case IDM_UserAdjudication_Draw:\r
5653         UserAdjudicationEvent( 0 );\r
5654         break;\r
5655 \r
5656     /* [AS] Game list options dialog */\r
5657     case IDM_GameListOptions:\r
5658       GameListOptions();\r
5659       break;\r
5660 \r
5661     case IDM_CopyPosition:\r
5662       CopyFENToClipboard();\r
5663       break;\r
5664 \r
5665     case IDM_PastePosition:\r
5666       PasteFENFromClipboard();\r
5667       break;\r
5668 \r
5669     case IDM_MailMove:\r
5670       MailMoveEvent();\r
5671       break;\r
5672 \r
5673     case IDM_ReloadCMailMsg:\r
5674       Reset(TRUE, TRUE);\r
5675       ReloadCmailMsgEvent(FALSE);\r
5676       break;\r
5677 \r
5678     case IDM_Minimize:\r
5679       ShowWindow(hwnd, SW_MINIMIZE);\r
5680       break;\r
5681 \r
5682     case IDM_Exit:\r
5683       ExitEvent(0);\r
5684       break;\r
5685 \r
5686     case IDM_MachineWhite:\r
5687       MachineWhiteEvent();\r
5688       /*\r
5689        * refresh the tags dialog only if it's visible\r
5690        */\r
5691       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5692           char *tags;\r
5693           tags = PGNTags(&gameInfo);\r
5694           TagsPopUp(tags, CmailMsg());\r
5695           free(tags);\r
5696       }\r
5697       break;\r
5698 \r
5699     case IDM_MachineBlack:\r
5700       MachineBlackEvent();\r
5701       /*\r
5702        * refresh the tags dialog only if it's visible\r
5703        */\r
5704       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5705           char *tags;\r
5706           tags = PGNTags(&gameInfo);\r
5707           TagsPopUp(tags, CmailMsg());\r
5708           free(tags);\r
5709       }\r
5710       break;\r
5711 \r
5712     case IDM_TwoMachines:\r
5713       TwoMachinesEvent();\r
5714       /*\r
5715        * refresh the tags dialog only if it's visible\r
5716        */\r
5717       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5718           char *tags;\r
5719           tags = PGNTags(&gameInfo);\r
5720           TagsPopUp(tags, CmailMsg());\r
5721           free(tags);\r
5722       }\r
5723       break;\r
5724 \r
5725     case IDM_AnalysisMode:\r
5726       if (!first.analysisSupport) {\r
5727         sprintf(buf, "%s does not support analysis", first.tidy);\r
5728         DisplayError(buf, 0);\r
5729       } else {\r
5730         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5731         if (appData.icsActive) {\r
5732                if (gameMode != IcsObserving) {\r
5733                        sprintf(buf, "You are not observing a game");\r
5734                        DisplayError(buf, 0);\r
5735                        /* secure check */\r
5736                        if (appData.icsEngineAnalyze) {\r
5737                                if (appData.debugMode) \r
5738                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5739                                ExitAnalyzeMode();\r
5740                                ModeHighlight();\r
5741                                break;\r
5742                        }\r
5743                        break;\r
5744                } else {\r
5745                        /* if enable, user want disable icsEngineAnalyze */\r
5746                        if (appData.icsEngineAnalyze) {\r
5747                                ExitAnalyzeMode();\r
5748                                ModeHighlight();\r
5749                                break;\r
5750                        }\r
5751                        appData.icsEngineAnalyze = TRUE;\r
5752                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5753                }\r
5754         } \r
5755         if (!appData.showThinking) ToggleShowThinking();\r
5756         AnalyzeModeEvent();\r
5757       }\r
5758       break;\r
5759 \r
5760     case IDM_AnalyzeFile:\r
5761       if (!first.analysisSupport) {\r
5762         char buf[MSG_SIZ];\r
5763         sprintf(buf, "%s does not support analysis", first.tidy);\r
5764         DisplayError(buf, 0);\r
5765       } else {\r
5766         if (!appData.showThinking) ToggleShowThinking();\r
5767         AnalyzeFileEvent();\r
5768         LoadGameDialog(hwnd, "Analyze Game from File");\r
5769         AnalysisPeriodicEvent(1);\r
5770       }\r
5771       break;\r
5772 \r
5773     case IDM_IcsClient:\r
5774       IcsClientEvent();\r
5775       break;\r
5776 \r
5777     case IDM_EditGame:\r
5778       EditGameEvent();\r
5779       break;\r
5780 \r
5781     case IDM_EditPosition:\r
5782       EditPositionEvent();\r
5783       break;\r
5784 \r
5785     case IDM_Training:\r
5786       TrainingEvent();\r
5787       break;\r
5788 \r
5789     case IDM_ShowGameList:\r
5790       ShowGameListProc();\r
5791       break;\r
5792 \r
5793     case IDM_EditTags:\r
5794       EditTagsProc();\r
5795       break;\r
5796 \r
5797     case IDM_EditComment:\r
5798       if (commentDialogUp && editComment) {\r
5799         CommentPopDown();\r
5800       } else {\r
5801         EditCommentEvent();\r
5802       }\r
5803       break;\r
5804 \r
5805     case IDM_Pause:\r
5806       PauseEvent();\r
5807       break;\r
5808 \r
5809     case IDM_Accept:\r
5810       AcceptEvent();\r
5811       break;\r
5812 \r
5813     case IDM_Decline:\r
5814       DeclineEvent();\r
5815       break;\r
5816 \r
5817     case IDM_Rematch:\r
5818       RematchEvent();\r
5819       break;\r
5820 \r
5821     case IDM_CallFlag:\r
5822       CallFlagEvent();\r
5823       break;\r
5824 \r
5825     case IDM_Draw:\r
5826       DrawEvent();\r
5827       break;\r
5828 \r
5829     case IDM_Adjourn:\r
5830       AdjournEvent();\r
5831       break;\r
5832 \r
5833     case IDM_Abort:\r
5834       AbortEvent();\r
5835       break;\r
5836 \r
5837     case IDM_Resign:\r
5838       ResignEvent();\r
5839       break;\r
5840 \r
5841     case IDM_StopObserving:\r
5842       StopObservingEvent();\r
5843       break;\r
5844 \r
5845     case IDM_StopExamining:\r
5846       StopExaminingEvent();\r
5847       break;\r
5848 \r
5849     case IDM_TypeInMove:\r
5850       PopUpMoveDialog('\000');\r
5851       break;\r
5852 \r
5853     case IDM_TypeInName:\r
5854       PopUpNameDialog('\000');\r
5855       break;\r
5856 \r
5857     case IDM_Backward:\r
5858       BackwardEvent();\r
5859       SetFocus(hwndMain);\r
5860       break;\r
5861 \r
5862     case IDM_Forward:\r
5863       ForwardEvent();\r
5864       SetFocus(hwndMain);\r
5865       break;\r
5866 \r
5867     case IDM_ToStart:\r
5868       ToStartEvent();\r
5869       SetFocus(hwndMain);\r
5870       break;\r
5871 \r
5872     case IDM_ToEnd:\r
5873       ToEndEvent();\r
5874       SetFocus(hwndMain);\r
5875       break;\r
5876 \r
5877     case IDM_Revert:\r
5878       RevertEvent();\r
5879       break;\r
5880 \r
5881     case IDM_TruncateGame:\r
5882       TruncateGameEvent();\r
5883       break;\r
5884 \r
5885     case IDM_MoveNow:\r
5886       MoveNowEvent();\r
5887       break;\r
5888 \r
5889     case IDM_RetractMove:\r
5890       RetractMoveEvent();\r
5891       break;\r
5892 \r
5893     case IDM_FlipView:\r
5894       flipView = !flipView;\r
5895       DrawPosition(FALSE, NULL);\r
5896       break;\r
5897 \r
5898     case IDM_FlipClock:\r
5899       flipClock = !flipClock;\r
5900       DisplayBothClocks();\r
5901       break;\r
5902 \r
5903     case IDM_GeneralOptions:\r
5904       GeneralOptionsPopup(hwnd);\r
5905       DrawPosition(TRUE, NULL);\r
5906       break;\r
5907 \r
5908     case IDM_BoardOptions:\r
5909       BoardOptionsPopup(hwnd);\r
5910       break;\r
5911 \r
5912     case IDM_EnginePlayOptions:\r
5913       EnginePlayOptionsPopup(hwnd);\r
5914       break;\r
5915 \r
5916     case IDM_OptionsUCI:\r
5917       UciOptionsPopup(hwnd);\r
5918       break;\r
5919 \r
5920     case IDM_IcsOptions:\r
5921       IcsOptionsPopup(hwnd);\r
5922       break;\r
5923 \r
5924     case IDM_Fonts:\r
5925       FontsOptionsPopup(hwnd);\r
5926       break;\r
5927 \r
5928     case IDM_Sounds:\r
5929       SoundOptionsPopup(hwnd);\r
5930       break;\r
5931 \r
5932     case IDM_CommPort:\r
5933       CommPortOptionsPopup(hwnd);\r
5934       break;\r
5935 \r
5936     case IDM_LoadOptions:\r
5937       LoadOptionsPopup(hwnd);\r
5938       break;\r
5939 \r
5940     case IDM_SaveOptions:\r
5941       SaveOptionsPopup(hwnd);\r
5942       break;\r
5943 \r
5944     case IDM_TimeControl:\r
5945       TimeControlOptionsPopup(hwnd);\r
5946       break;\r
5947 \r
5948     case IDM_SaveSettings:\r
5949       SaveSettings(settingsFileName);\r
5950       break;\r
5951 \r
5952     case IDM_SaveSettingsOnExit:\r
5953       saveSettingsOnExit = !saveSettingsOnExit;\r
5954       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5955                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5956                                          MF_CHECKED : MF_UNCHECKED));\r
5957       break;\r
5958 \r
5959     case IDM_Hint:\r
5960       HintEvent();\r
5961       break;\r
5962 \r
5963     case IDM_Book:\r
5964       BookEvent();\r
5965       break;\r
5966 \r
5967     case IDM_AboutGame:\r
5968       AboutGameEvent();\r
5969       break;\r
5970 \r
5971     case IDM_Debug:\r
5972       appData.debugMode = !appData.debugMode;\r
5973       if (appData.debugMode) {\r
5974         char dir[MSG_SIZ];\r
5975         GetCurrentDirectory(MSG_SIZ, dir);\r
5976         SetCurrentDirectory(installDir);\r
5977         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5978         SetCurrentDirectory(dir);\r
5979         setbuf(debugFP, NULL);\r
5980       } else {\r
5981         fclose(debugFP);\r
5982         debugFP = NULL;\r
5983       }\r
5984       break;\r
5985 \r
5986     case IDM_HELPCONTENTS:\r
5987       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5988         MessageBox (GetFocus(),\r
5989                     "Unable to activate help",\r
5990                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5991       }\r
5992       break;\r
5993 \r
5994     case IDM_HELPSEARCH:\r
5995       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5996         MessageBox (GetFocus(),\r
5997                     "Unable to activate help",\r
5998                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5999       }\r
6000       break;\r
6001 \r
6002     case IDM_HELPHELP:\r
6003       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6004         MessageBox (GetFocus(),\r
6005                     "Unable to activate help",\r
6006                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6007       }\r
6008       break;\r
6009 \r
6010     case IDM_ABOUT:\r
6011       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6012       DialogBox(hInst, \r
6013         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6014         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6015       FreeProcInstance(lpProc);\r
6016       break;\r
6017 \r
6018     case IDM_DirectCommand1:\r
6019       AskQuestionEvent("Direct Command",\r
6020                        "Send to chess program:", "", "1");\r
6021       break;\r
6022     case IDM_DirectCommand2:\r
6023       AskQuestionEvent("Direct Command",\r
6024                        "Send to second chess program:", "", "2");\r
6025       break;\r
6026 \r
6027     case EP_WhitePawn:\r
6028       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6029       fromX = fromY = -1;\r
6030       break;\r
6031 \r
6032     case EP_WhiteKnight:\r
6033       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6034       fromX = fromY = -1;\r
6035       break;\r
6036 \r
6037     case EP_WhiteBishop:\r
6038       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6039       fromX = fromY = -1;\r
6040       break;\r
6041 \r
6042     case EP_WhiteRook:\r
6043       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6044       fromX = fromY = -1;\r
6045       break;\r
6046 \r
6047     case EP_WhiteQueen:\r
6048       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6049       fromX = fromY = -1;\r
6050       break;\r
6051 \r
6052     case EP_WhiteFerz:\r
6053       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6054       fromX = fromY = -1;\r
6055       break;\r
6056 \r
6057     case EP_WhiteWazir:\r
6058       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6059       fromX = fromY = -1;\r
6060       break;\r
6061 \r
6062     case EP_WhiteAlfil:\r
6063       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6064       fromX = fromY = -1;\r
6065       break;\r
6066 \r
6067     case EP_WhiteCannon:\r
6068       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6069       fromX = fromY = -1;\r
6070       break;\r
6071 \r
6072     case EP_WhiteCardinal:\r
6073       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6074       fromX = fromY = -1;\r
6075       break;\r
6076 \r
6077     case EP_WhiteMarshall:\r
6078       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6079       fromX = fromY = -1;\r
6080       break;\r
6081 \r
6082     case EP_WhiteKing:\r
6083       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6084       fromX = fromY = -1;\r
6085       break;\r
6086 \r
6087     case EP_BlackPawn:\r
6088       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6089       fromX = fromY = -1;\r
6090       break;\r
6091 \r
6092     case EP_BlackKnight:\r
6093       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6094       fromX = fromY = -1;\r
6095       break;\r
6096 \r
6097     case EP_BlackBishop:\r
6098       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6099       fromX = fromY = -1;\r
6100       break;\r
6101 \r
6102     case EP_BlackRook:\r
6103       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6104       fromX = fromY = -1;\r
6105       break;\r
6106 \r
6107     case EP_BlackQueen:\r
6108       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6109       fromX = fromY = -1;\r
6110       break;\r
6111 \r
6112     case EP_BlackFerz:\r
6113       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6114       fromX = fromY = -1;\r
6115       break;\r
6116 \r
6117     case EP_BlackWazir:\r
6118       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6119       fromX = fromY = -1;\r
6120       break;\r
6121 \r
6122     case EP_BlackAlfil:\r
6123       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6124       fromX = fromY = -1;\r
6125       break;\r
6126 \r
6127     case EP_BlackCannon:\r
6128       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6129       fromX = fromY = -1;\r
6130       break;\r
6131 \r
6132     case EP_BlackCardinal:\r
6133       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6134       fromX = fromY = -1;\r
6135       break;\r
6136 \r
6137     case EP_BlackMarshall:\r
6138       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6139       fromX = fromY = -1;\r
6140       break;\r
6141 \r
6142     case EP_BlackKing:\r
6143       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6144       fromX = fromY = -1;\r
6145       break;\r
6146 \r
6147     case EP_EmptySquare:\r
6148       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6149       fromX = fromY = -1;\r
6150       break;\r
6151 \r
6152     case EP_ClearBoard:\r
6153       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6154       fromX = fromY = -1;\r
6155       break;\r
6156 \r
6157     case EP_White:\r
6158       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6159       fromX = fromY = -1;\r
6160       break;\r
6161 \r
6162     case EP_Black:\r
6163       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6164       fromX = fromY = -1;\r
6165       break;\r
6166 \r
6167     case EP_Promote:\r
6168       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6169       fromX = fromY = -1;\r
6170       break;\r
6171 \r
6172     case EP_Demote:\r
6173       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6174       fromX = fromY = -1;\r
6175       break;\r
6176 \r
6177     case DP_Pawn:\r
6178       DropMenuEvent(WhitePawn, fromX, fromY);\r
6179       fromX = fromY = -1;\r
6180       break;\r
6181 \r
6182     case DP_Knight:\r
6183       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6184       fromX = fromY = -1;\r
6185       break;\r
6186 \r
6187     case DP_Bishop:\r
6188       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6189       fromX = fromY = -1;\r
6190       break;\r
6191 \r
6192     case DP_Rook:\r
6193       DropMenuEvent(WhiteRook, fromX, fromY);\r
6194       fromX = fromY = -1;\r
6195       break;\r
6196 \r
6197     case DP_Queen:\r
6198       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6199       fromX = fromY = -1;\r
6200       break;\r
6201 \r
6202     default:\r
6203       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6204     }\r
6205     break;\r
6206 \r
6207   case WM_TIMER:\r
6208     switch (wParam) {\r
6209     case CLOCK_TIMER_ID:\r
6210       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6211       clockTimerEvent = 0;\r
6212       DecrementClocks(); /* call into back end */\r
6213       break;\r
6214     case LOAD_GAME_TIMER_ID:\r
6215       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6216       loadGameTimerEvent = 0;\r
6217       AutoPlayGameLoop(); /* call into back end */\r
6218       break;\r
6219     case ANALYSIS_TIMER_ID:\r
6220       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6221                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6222         AnalysisPeriodicEvent(0);\r
6223       } else {\r
6224         KillTimer(hwnd, analysisTimerEvent);\r
6225         analysisTimerEvent = 0;\r
6226       }\r
6227       break;\r
6228     case DELAYED_TIMER_ID:\r
6229       KillTimer(hwnd, delayedTimerEvent);\r
6230       delayedTimerEvent = 0;\r
6231       delayedTimerCallback();\r
6232       break;\r
6233     }\r
6234     break;\r
6235 \r
6236   case WM_USER_Input:\r
6237     InputEvent(hwnd, message, wParam, lParam);\r
6238     break;\r
6239 \r
6240   /* [AS] Also move "attached" child windows */\r
6241   case WM_WINDOWPOSCHANGING:\r
6242     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6243         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6244 \r
6245         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6246             /* Window is moving */\r
6247             RECT rcMain;\r
6248 \r
6249             GetWindowRect( hwnd, &rcMain );\r
6250             \r
6251             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6252             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6253             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6254         }\r
6255     }\r
6256     break;\r
6257 \r
6258   /* [AS] Snapping */\r
6259   case WM_ENTERSIZEMOVE:\r
6260     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6261     if (hwnd == hwndMain) {\r
6262       doingSizing = TRUE;\r
6263       lastSizing = 0;\r
6264     }\r
6265     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6266     break;\r
6267 \r
6268   case WM_SIZING:\r
6269     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6270     if (hwnd == hwndMain) {\r
6271       lastSizing = wParam;\r
6272     }\r
6273     break;\r
6274 \r
6275   case WM_MOVING:\r
6276     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6277       return OnMoving( &sd, hwnd, wParam, lParam );\r
6278 \r
6279   case WM_EXITSIZEMOVE:\r
6280     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6281     if (hwnd == hwndMain) {\r
6282       RECT client;\r
6283       doingSizing = FALSE;\r
6284       InvalidateRect(hwnd, &boardRect, FALSE);\r
6285       GetClientRect(hwnd, &client);\r
6286       ResizeBoard(client.right, client.bottom, lastSizing);\r
6287       lastSizing = 0;\r
6288       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6289     }\r
6290     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6291     break;\r
6292 \r
6293   case WM_DESTROY: /* message: window being destroyed */\r
6294     PostQuitMessage(0);\r
6295     break;\r
6296 \r
6297   case WM_CLOSE:\r
6298     if (hwnd == hwndMain) {\r
6299       ExitEvent(0);\r
6300     }\r
6301     break;\r
6302 \r
6303   default:      /* Passes it on if unprocessed */\r
6304     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6305   }\r
6306   return 0;\r
6307 }\r
6308 \r
6309 /*---------------------------------------------------------------------------*\\r
6310  *\r
6311  * Misc utility routines\r
6312  *\r
6313 \*---------------------------------------------------------------------------*/\r
6314 \r
6315 /*\r
6316  * Decent random number generator, at least not as bad as Windows\r
6317  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6318  */\r
6319 unsigned int randstate;\r
6320 \r
6321 int\r
6322 myrandom(void)\r
6323 {\r
6324   randstate = randstate * 1664525 + 1013904223;\r
6325   return (int) randstate & 0x7fffffff;\r
6326 }\r
6327 \r
6328 void\r
6329 mysrandom(unsigned int seed)\r
6330 {\r
6331   randstate = seed;\r
6332 }\r
6333 \r
6334 \r
6335 /* \r
6336  * returns TRUE if user selects a different color, FALSE otherwise \r
6337  */\r
6338 \r
6339 BOOL\r
6340 ChangeColor(HWND hwnd, COLORREF *which)\r
6341 {\r
6342   static BOOL firstTime = TRUE;\r
6343   static DWORD customColors[16];\r
6344   CHOOSECOLOR cc;\r
6345   COLORREF newcolor;\r
6346   int i;\r
6347   ColorClass ccl;\r
6348 \r
6349   if (firstTime) {\r
6350     /* Make initial colors in use available as custom colors */\r
6351     /* Should we put the compiled-in defaults here instead? */\r
6352     i = 0;\r
6353     customColors[i++] = lightSquareColor & 0xffffff;\r
6354     customColors[i++] = darkSquareColor & 0xffffff;\r
6355     customColors[i++] = whitePieceColor & 0xffffff;\r
6356     customColors[i++] = blackPieceColor & 0xffffff;\r
6357     customColors[i++] = highlightSquareColor & 0xffffff;\r
6358     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6359 \r
6360     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6361       customColors[i++] = textAttribs[ccl].color;\r
6362     }\r
6363     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6364     firstTime = FALSE;\r
6365   }\r
6366 \r
6367   cc.lStructSize = sizeof(cc);\r
6368   cc.hwndOwner = hwnd;\r
6369   cc.hInstance = NULL;\r
6370   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6371   cc.lpCustColors = (LPDWORD) customColors;\r
6372   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6373 \r
6374   if (!ChooseColor(&cc)) return FALSE;\r
6375 \r
6376   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6377   if (newcolor == *which) return FALSE;\r
6378   *which = newcolor;\r
6379   return TRUE;\r
6380 \r
6381   /*\r
6382   InitDrawingColors();\r
6383   InvalidateRect(hwnd, &boardRect, FALSE);\r
6384   */\r
6385 }\r
6386 \r
6387 BOOLEAN\r
6388 MyLoadSound(MySound *ms)\r
6389 {\r
6390   BOOL ok = FALSE;\r
6391   struct stat st;\r
6392   FILE *f;\r
6393 \r
6394   if (ms->data) free(ms->data);\r
6395   ms->data = NULL;\r
6396 \r
6397   switch (ms->name[0]) {\r
6398   case NULLCHAR:\r
6399     /* Silence */\r
6400     ok = TRUE;\r
6401     break;\r
6402   case '$':\r
6403     /* System sound from Control Panel.  Don't preload here. */\r
6404     ok = TRUE;\r
6405     break;\r
6406   case '!':\r
6407     if (ms->name[1] == NULLCHAR) {\r
6408       /* "!" alone = silence */\r
6409       ok = TRUE;\r
6410     } else {\r
6411       /* Builtin wave resource.  Error if not found. */\r
6412       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6413       if (h == NULL) break;\r
6414       ms->data = (void *)LoadResource(hInst, h);\r
6415       if (h == NULL) break;\r
6416       ok = TRUE;\r
6417     }\r
6418     break;\r
6419   default:\r
6420     /* .wav file.  Error if not found. */\r
6421     f = fopen(ms->name, "rb");\r
6422     if (f == NULL) break;\r
6423     if (fstat(fileno(f), &st) < 0) break;\r
6424     ms->data = malloc(st.st_size);\r
6425     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6426     fclose(f);\r
6427     ok = TRUE;\r
6428     break;\r
6429   }\r
6430   if (!ok) {\r
6431     char buf[MSG_SIZ];\r
6432     sprintf(buf, "Error loading sound %s", ms->name);\r
6433     DisplayError(buf, GetLastError());\r
6434   }\r
6435   return ok;\r
6436 }\r
6437 \r
6438 BOOLEAN\r
6439 MyPlaySound(MySound *ms)\r
6440 {\r
6441   BOOLEAN ok = FALSE;\r
6442   switch (ms->name[0]) {\r
6443   case NULLCHAR:\r
6444     /* Silence */\r
6445     ok = TRUE;\r
6446     break;\r
6447   case '$':\r
6448     /* System sound from Control Panel (deprecated feature).\r
6449        "$" alone or an unset sound name gets default beep (still in use). */\r
6450     if (ms->name[1]) {\r
6451       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6452     }\r
6453     if (!ok) ok = MessageBeep(MB_OK);\r
6454     break; \r
6455   case '!':\r
6456     /* Builtin wave resource, or "!" alone for silence */\r
6457     if (ms->name[1]) {\r
6458       if (ms->data == NULL) return FALSE;\r
6459       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6460     } else {\r
6461       ok = TRUE;\r
6462     }\r
6463     break;\r
6464   default:\r
6465     /* .wav file.  Error if not found. */\r
6466     if (ms->data == NULL) return FALSE;\r
6467     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6468     break;\r
6469   }\r
6470   /* Don't print an error: this can happen innocently if the sound driver\r
6471      is busy; for instance, if another instance of WinBoard is playing\r
6472      a sound at about the same time. */\r
6473 #if 0\r
6474   if (!ok) {\r
6475     char buf[MSG_SIZ];\r
6476     sprintf(buf, "Error playing sound %s", ms->name);\r
6477     DisplayError(buf, GetLastError());\r
6478   }\r
6479 #endif\r
6480   return ok;\r
6481 }\r
6482 \r
6483 \r
6484 LRESULT CALLBACK\r
6485 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6486 {\r
6487   BOOL ok;\r
6488   OPENFILENAME *ofn;\r
6489   static UINT *number; /* gross that this is static */\r
6490 \r
6491   switch (message) {\r
6492   case WM_INITDIALOG: /* message: initialize dialog box */\r
6493     /* Center the dialog over the application window */\r
6494     ofn = (OPENFILENAME *) lParam;\r
6495     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6496       number = (UINT *) ofn->lCustData;\r
6497       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6498     } else {\r
6499       number = NULL;\r
6500     }\r
6501     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6502     return FALSE;  /* Allow for further processing */\r
6503 \r
6504   case WM_COMMAND:\r
6505     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6506       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6507     }\r
6508     return FALSE;  /* Allow for further processing */\r
6509   }\r
6510   return FALSE;\r
6511 }\r
6512 \r
6513 UINT APIENTRY\r
6514 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6515 {\r
6516   static UINT *number;\r
6517   OPENFILENAME *ofname;\r
6518   OFNOTIFY *ofnot;\r
6519   switch (uiMsg) {\r
6520   case WM_INITDIALOG:\r
6521     ofname = (OPENFILENAME *)lParam;\r
6522     number = (UINT *)(ofname->lCustData);\r
6523     break;\r
6524   case WM_NOTIFY:\r
6525     ofnot = (OFNOTIFY *)lParam;\r
6526     if (ofnot->hdr.code == CDN_FILEOK) {\r
6527       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6528     }\r
6529     break;\r
6530   }\r
6531   return 0;\r
6532 }\r
6533 \r
6534 \r
6535 FILE *\r
6536 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6537                char *nameFilt, char *dlgTitle, UINT *number,\r
6538                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6539 {\r
6540   OPENFILENAME openFileName;\r
6541   char buf1[MSG_SIZ];\r
6542   FILE *f;\r
6543 \r
6544   if (fileName == NULL) fileName = buf1;\r
6545   if (defName == NULL) {\r
6546     strcpy(fileName, "*.");\r
6547     strcat(fileName, defExt);\r
6548   } else {\r
6549     strcpy(fileName, defName);\r
6550   }\r
6551   if (fileTitle) strcpy(fileTitle, "");\r
6552   if (number) *number = 0;\r
6553 \r
6554   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6555   openFileName.hwndOwner         = hwnd;\r
6556   openFileName.hInstance         = (HANDLE) hInst;\r
6557   openFileName.lpstrFilter       = nameFilt;\r
6558   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6559   openFileName.nMaxCustFilter    = 0L;\r
6560   openFileName.nFilterIndex      = 1L;\r
6561   openFileName.lpstrFile         = fileName;\r
6562   openFileName.nMaxFile          = MSG_SIZ;\r
6563   openFileName.lpstrFileTitle    = fileTitle;\r
6564   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6565   openFileName.lpstrInitialDir   = NULL;\r
6566   openFileName.lpstrTitle        = dlgTitle;\r
6567   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6568     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6569     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6570     | (oldDialog ? 0 : OFN_EXPLORER);\r
6571   openFileName.nFileOffset       = 0;\r
6572   openFileName.nFileExtension    = 0;\r
6573   openFileName.lpstrDefExt       = defExt;\r
6574   openFileName.lCustData         = (LONG) number;\r
6575   openFileName.lpfnHook          = oldDialog ?\r
6576     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6577   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6578 \r
6579   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6580                         GetOpenFileName(&openFileName)) {\r
6581     /* open the file */\r
6582     f = fopen(openFileName.lpstrFile, write);\r
6583     if (f == NULL) {\r
6584       MessageBox(hwnd, "File open failed", NULL,\r
6585                  MB_OK|MB_ICONEXCLAMATION);\r
6586       return NULL;\r
6587     }\r
6588   } else {\r
6589     int err = CommDlgExtendedError();\r
6590     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6591     return FALSE;\r
6592   }\r
6593   return f;\r
6594 }\r
6595 \r
6596 \r
6597 \r
6598 VOID APIENTRY\r
6599 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6600 {\r
6601   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6602 \r
6603   /*\r
6604    * Get the first pop-up menu in the menu template. This is the\r
6605    * menu that TrackPopupMenu displays.\r
6606    */\r
6607   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6608 \r
6609   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6610 \r
6611   /*\r
6612    * TrackPopup uses screen coordinates, so convert the\r
6613    * coordinates of the mouse click to screen coordinates.\r
6614    */\r
6615   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6616 \r
6617   /* Draw and track the floating pop-up menu. */\r
6618   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6619                  pt.x, pt.y, 0, hwnd, NULL);\r
6620 \r
6621   /* Destroy the menu.*/\r
6622   DestroyMenu(hmenu);\r
6623 }\r
6624    \r
6625 typedef struct {\r
6626   HWND hDlg, hText;\r
6627   int sizeX, sizeY, newSizeX, newSizeY;\r
6628   HDWP hdwp;\r
6629 } ResizeEditPlusButtonsClosure;\r
6630 \r
6631 BOOL CALLBACK\r
6632 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6633 {\r
6634   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6635   RECT rect;\r
6636   POINT pt;\r
6637 \r
6638   if (hChild == cl->hText) return TRUE;\r
6639   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6640   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6641   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6642   ScreenToClient(cl->hDlg, &pt);\r
6643   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6644     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6645   return TRUE;\r
6646 }\r
6647 \r
6648 /* Resize a dialog that has a (rich) edit field filling most of\r
6649    the top, with a row of buttons below */\r
6650 VOID\r
6651 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6652 {\r
6653   RECT rectText;\r
6654   int newTextHeight, newTextWidth;\r
6655   ResizeEditPlusButtonsClosure cl;\r
6656   \r
6657   /*if (IsIconic(hDlg)) return;*/\r
6658   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6659   \r
6660   cl.hdwp = BeginDeferWindowPos(8);\r
6661 \r
6662   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6663   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6664   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6665   if (newTextHeight < 0) {\r
6666     newSizeY += -newTextHeight;\r
6667     newTextHeight = 0;\r
6668   }\r
6669   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6670     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6671 \r
6672   cl.hDlg = hDlg;\r
6673   cl.hText = hText;\r
6674   cl.sizeX = sizeX;\r
6675   cl.sizeY = sizeY;\r
6676   cl.newSizeX = newSizeX;\r
6677   cl.newSizeY = newSizeY;\r
6678   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6679 \r
6680   EndDeferWindowPos(cl.hdwp);\r
6681 }\r
6682 \r
6683 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6684 {\r
6685     RECT    rChild, rParent;\r
6686     int     wChild, hChild, wParent, hParent;\r
6687     int     wScreen, hScreen, xNew, yNew;\r
6688     HDC     hdc;\r
6689 \r
6690     /* Get the Height and Width of the child window */\r
6691     GetWindowRect (hwndChild, &rChild);\r
6692     wChild = rChild.right - rChild.left;\r
6693     hChild = rChild.bottom - rChild.top;\r
6694 \r
6695     /* Get the Height and Width of the parent window */\r
6696     GetWindowRect (hwndParent, &rParent);\r
6697     wParent = rParent.right - rParent.left;\r
6698     hParent = rParent.bottom - rParent.top;\r
6699 \r
6700     /* Get the display limits */\r
6701     hdc = GetDC (hwndChild);\r
6702     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6703     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6704     ReleaseDC(hwndChild, hdc);\r
6705 \r
6706     /* Calculate new X position, then adjust for screen */\r
6707     xNew = rParent.left + ((wParent - wChild) /2);\r
6708     if (xNew < 0) {\r
6709         xNew = 0;\r
6710     } else if ((xNew+wChild) > wScreen) {\r
6711         xNew = wScreen - wChild;\r
6712     }\r
6713 \r
6714     /* Calculate new Y position, then adjust for screen */\r
6715     if( mode == 0 ) {\r
6716         yNew = rParent.top  + ((hParent - hChild) /2);\r
6717     }\r
6718     else {\r
6719         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6720     }\r
6721 \r
6722     if (yNew < 0) {\r
6723         yNew = 0;\r
6724     } else if ((yNew+hChild) > hScreen) {\r
6725         yNew = hScreen - hChild;\r
6726     }\r
6727 \r
6728     /* Set it, and return */\r
6729     return SetWindowPos (hwndChild, NULL,\r
6730                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6731 }\r
6732 \r
6733 /* Center one window over another */\r
6734 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6735 {\r
6736     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6737 }\r
6738 \r
6739 /*---------------------------------------------------------------------------*\\r
6740  *\r
6741  * Startup Dialog functions\r
6742  *\r
6743 \*---------------------------------------------------------------------------*/\r
6744 void\r
6745 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6746 {\r
6747   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6748 \r
6749   while (*cd != NULL) {\r
6750     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6751     cd++;\r
6752   }\r
6753 }\r
6754 \r
6755 void\r
6756 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6757 {\r
6758   char buf1[ARG_MAX];\r
6759   int len;\r
6760 \r
6761   if (str[0] == '@') {\r
6762     FILE* f = fopen(str + 1, "r");\r
6763     if (f == NULL) {\r
6764       DisplayFatalError(str + 1, errno, 2);\r
6765       return;\r
6766     }\r
6767     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6768     fclose(f);\r
6769     buf1[len] = NULLCHAR;\r
6770     str = buf1;\r
6771   }\r
6772 \r
6773   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6774 \r
6775   for (;;) {\r
6776     char buf[MSG_SIZ];\r
6777     char *end = strchr(str, '\n');\r
6778     if (end == NULL) return;\r
6779     memcpy(buf, str, end - str);\r
6780     buf[end - str] = NULLCHAR;\r
6781     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6782     str = end + 1;\r
6783   }\r
6784 }\r
6785 \r
6786 void\r
6787 SetStartupDialogEnables(HWND hDlg)\r
6788 {\r
6789   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6790     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6791     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6792   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6793     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6794   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6795     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6796   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6797     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6798   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6799     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6800     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6801     IsDlgButtonChecked(hDlg, OPT_View));\r
6802 }\r
6803 \r
6804 char *\r
6805 QuoteForFilename(char *filename)\r
6806 {\r
6807   int dquote, space;\r
6808   dquote = strchr(filename, '"') != NULL;\r
6809   space = strchr(filename, ' ') != NULL;\r
6810   if (dquote || space) {\r
6811     if (dquote) {\r
6812       return "'";\r
6813     } else {\r
6814       return "\"";\r
6815     }\r
6816   } else {\r
6817     return "";\r
6818   }\r
6819 }\r
6820 \r
6821 VOID\r
6822 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6823 {\r
6824   char buf[MSG_SIZ];\r
6825   char *q;\r
6826 \r
6827   InitComboStringsFromOption(hwndCombo, nthnames);\r
6828   q = QuoteForFilename(nthcp);\r
6829   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6830   if (*nthdir != NULLCHAR) {\r
6831     q = QuoteForFilename(nthdir);\r
6832     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6833   }\r
6834   if (*nthcp == NULLCHAR) {\r
6835     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6836   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6837     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6838     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6839   }\r
6840 }\r
6841 \r
6842 LRESULT CALLBACK\r
6843 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6844 {\r
6845   char buf[MSG_SIZ];\r
6846   HANDLE hwndCombo;\r
6847   char *p;\r
6848 \r
6849   switch (message) {\r
6850   case WM_INITDIALOG:\r
6851     /* Center the dialog */\r
6852     CenterWindow (hDlg, GetDesktopWindow());\r
6853     /* Initialize the dialog items */\r
6854     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6855                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6856                   firstChessProgramNames);\r
6857     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6858                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6859                   secondChessProgramNames);\r
6860     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6861     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6862     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6863     if (*appData.icsHelper != NULLCHAR) {\r
6864       char *q = QuoteForFilename(appData.icsHelper);\r
6865       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6866     }\r
6867     if (*appData.icsHost == NULLCHAR) {\r
6868       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6869       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6870     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6871       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6872       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6873     }\r
6874 \r
6875     if (appData.icsActive) {\r
6876       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6877     }\r
6878     else if (appData.noChessProgram) {\r
6879       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6880     }\r
6881     else {\r
6882       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6883     }\r
6884 \r
6885     SetStartupDialogEnables(hDlg);\r
6886     return TRUE;\r
6887 \r
6888   case WM_COMMAND:\r
6889     switch (LOWORD(wParam)) {\r
6890     case IDOK:\r
6891       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6892         strcpy(buf, "/fcp=");\r
6893         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6894         p = buf;\r
6895         ParseArgs(StringGet, &p);\r
6896         strcpy(buf, "/scp=");\r
6897         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6898         p = buf;\r
6899         ParseArgs(StringGet, &p);\r
6900         appData.noChessProgram = FALSE;\r
6901         appData.icsActive = FALSE;\r
6902       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6903         strcpy(buf, "/ics /icshost=");\r
6904         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6905         p = buf;\r
6906         ParseArgs(StringGet, &p);\r
6907         if (appData.zippyPlay) {\r
6908           strcpy(buf, "/fcp=");\r
6909           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6910           p = buf;\r
6911           ParseArgs(StringGet, &p);\r
6912         }\r
6913       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6914         appData.noChessProgram = TRUE;\r
6915         appData.icsActive = FALSE;\r
6916       } else {\r
6917         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6918                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6919         return TRUE;\r
6920       }\r
6921       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6922         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6923         p = buf;\r
6924         ParseArgs(StringGet, &p);\r
6925       }\r
6926       EndDialog(hDlg, TRUE);\r
6927       return TRUE;\r
6928 \r
6929     case IDCANCEL:\r
6930       ExitEvent(0);\r
6931       return TRUE;\r
6932 \r
6933     case IDM_HELPCONTENTS:\r
6934       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6935         MessageBox (GetFocus(),\r
6936                     "Unable to activate help",\r
6937                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6938       }\r
6939       break;\r
6940 \r
6941     default:\r
6942       SetStartupDialogEnables(hDlg);\r
6943       break;\r
6944     }\r
6945     break;\r
6946   }\r
6947   return FALSE;\r
6948 }\r
6949 \r
6950 /*---------------------------------------------------------------------------*\\r
6951  *\r
6952  * About box dialog functions\r
6953  *\r
6954 \*---------------------------------------------------------------------------*/\r
6955 \r
6956 /* Process messages for "About" dialog box */\r
6957 LRESULT CALLBACK\r
6958 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6959 {\r
6960   switch (message) {\r
6961   case WM_INITDIALOG: /* message: initialize dialog box */\r
6962     /* Center the dialog over the application window */\r
6963     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6964     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6965     return (TRUE);\r
6966 \r
6967   case WM_COMMAND: /* message: received a command */\r
6968     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6969         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6970       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6971       return (TRUE);\r
6972     }\r
6973     break;\r
6974   }\r
6975   return (FALSE);\r
6976 }\r
6977 \r
6978 /*---------------------------------------------------------------------------*\\r
6979  *\r
6980  * Comment Dialog functions\r
6981  *\r
6982 \*---------------------------------------------------------------------------*/\r
6983 \r
6984 LRESULT CALLBACK\r
6985 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6986 {\r
6987   static HANDLE hwndText = NULL;\r
6988   int len, newSizeX, newSizeY, flags;\r
6989   static int sizeX, sizeY;\r
6990   char *str;\r
6991   RECT rect;\r
6992   MINMAXINFO *mmi;\r
6993 \r
6994   switch (message) {\r
6995   case WM_INITDIALOG: /* message: initialize dialog box */\r
6996     /* Initialize the dialog items */\r
6997     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6998     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6999     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7000     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7001     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7002     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7003     SetWindowText(hDlg, commentTitle);\r
7004     if (editComment) {\r
7005       SetFocus(hwndText);\r
7006     } else {\r
7007       SetFocus(GetDlgItem(hDlg, IDOK));\r
7008     }\r
7009     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7010                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7011                 MAKELPARAM(FALSE, 0));\r
7012     /* Size and position the dialog */\r
7013     if (!commentDialog) {\r
7014       commentDialog = hDlg;\r
7015       flags = SWP_NOZORDER;\r
7016       GetClientRect(hDlg, &rect);\r
7017       sizeX = rect.right;\r
7018       sizeY = rect.bottom;\r
7019       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7020           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7021         WINDOWPLACEMENT wp;\r
7022         EnsureOnScreen(&commentX, &commentY);\r
7023         wp.length = sizeof(WINDOWPLACEMENT);\r
7024         wp.flags = 0;\r
7025         wp.showCmd = SW_SHOW;\r
7026         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7027         wp.rcNormalPosition.left = commentX;\r
7028         wp.rcNormalPosition.right = commentX + commentW;\r
7029         wp.rcNormalPosition.top = commentY;\r
7030         wp.rcNormalPosition.bottom = commentY + commentH;\r
7031         SetWindowPlacement(hDlg, &wp);\r
7032 \r
7033         GetClientRect(hDlg, &rect);\r
7034         newSizeX = rect.right;\r
7035         newSizeY = rect.bottom;\r
7036         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7037                               newSizeX, newSizeY);\r
7038         sizeX = newSizeX;\r
7039         sizeY = newSizeY;\r
7040       }\r
7041     }\r
7042     return FALSE;\r
7043 \r
7044   case WM_COMMAND: /* message: received a command */\r
7045     switch (LOWORD(wParam)) {\r
7046     case IDOK:\r
7047       if (editComment) {\r
7048         char *p, *q;\r
7049         /* Read changed options from the dialog box */\r
7050         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7051         len = GetWindowTextLength(hwndText);\r
7052         str = (char *) malloc(len + 1);\r
7053         GetWindowText(hwndText, str, len + 1);\r
7054         p = q = str;\r
7055         while (*q) {\r
7056           if (*q == '\r')\r
7057             q++;\r
7058           else\r
7059             *p++ = *q++;\r
7060         }\r
7061         *p = NULLCHAR;\r
7062         ReplaceComment(commentIndex, str);\r
7063         free(str);\r
7064       }\r
7065       CommentPopDown();\r
7066       return TRUE;\r
7067 \r
7068     case IDCANCEL:\r
7069     case OPT_CancelComment:\r
7070       CommentPopDown();\r
7071       return TRUE;\r
7072 \r
7073     case OPT_ClearComment:\r
7074       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7075       break;\r
7076 \r
7077     case OPT_EditComment:\r
7078       EditCommentEvent();\r
7079       return TRUE;\r
7080 \r
7081     default:\r
7082       break;\r
7083     }\r
7084     break;\r
7085 \r
7086   case WM_SIZE:\r
7087     newSizeX = LOWORD(lParam);\r
7088     newSizeY = HIWORD(lParam);\r
7089     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7090     sizeX = newSizeX;\r
7091     sizeY = newSizeY;\r
7092     break;\r
7093 \r
7094   case WM_GETMINMAXINFO:\r
7095     /* Prevent resizing window too small */\r
7096     mmi = (MINMAXINFO *) lParam;\r
7097     mmi->ptMinTrackSize.x = 100;\r
7098     mmi->ptMinTrackSize.y = 100;\r
7099     break;\r
7100   }\r
7101   return FALSE;\r
7102 }\r
7103 \r
7104 VOID\r
7105 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7106 {\r
7107   FARPROC lpProc;\r
7108   char *p, *q;\r
7109 \r
7110   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7111 \r
7112   if (str == NULL) str = "";\r
7113   p = (char *) malloc(2 * strlen(str) + 2);\r
7114   q = p;\r
7115   while (*str) {\r
7116     if (*str == '\n') *q++ = '\r';\r
7117     *q++ = *str++;\r
7118   }\r
7119   *q = NULLCHAR;\r
7120   if (commentText != NULL) free(commentText);\r
7121 \r
7122   commentIndex = index;\r
7123   commentTitle = title;\r
7124   commentText = p;\r
7125   editComment = edit;\r
7126 \r
7127   if (commentDialog) {\r
7128     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7129     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7130   } else {\r
7131     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7132     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7133                  hwndMain, (DLGPROC)lpProc);\r
7134     FreeProcInstance(lpProc);\r
7135   }\r
7136   commentDialogUp = TRUE;\r
7137 }\r
7138 \r
7139 \r
7140 /*---------------------------------------------------------------------------*\\r
7141  *\r
7142  * Type-in move dialog functions\r
7143  * \r
7144 \*---------------------------------------------------------------------------*/\r
7145 \r
7146 LRESULT CALLBACK\r
7147 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7148 {\r
7149   char move[MSG_SIZ];\r
7150   HWND hInput;\r
7151   ChessMove moveType;\r
7152   int fromX, fromY, toX, toY;\r
7153   char promoChar;\r
7154 \r
7155   switch (message) {\r
7156   case WM_INITDIALOG:\r
7157     move[0] = (char) lParam;\r
7158     move[1] = NULLCHAR;\r
7159     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7160     hInput = GetDlgItem(hDlg, OPT_Move);\r
7161     SetWindowText(hInput, move);\r
7162     SetFocus(hInput);\r
7163     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7164     return FALSE;\r
7165 \r
7166   case WM_COMMAND:\r
7167     switch (LOWORD(wParam)) {\r
7168     case IDOK:\r
7169       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7170         gameMode != Training) {\r
7171         DisplayMoveError("Displayed move is not current");\r
7172       } else {\r
7173         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7174         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7175           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7176           if (gameMode != Training)\r
7177               forwardMostMove = currentMove;\r
7178           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7179         } else {\r
7180           DisplayMoveError("Could not parse move");\r
7181         }\r
7182       }\r
7183       EndDialog(hDlg, TRUE);\r
7184       return TRUE;\r
7185     case IDCANCEL:\r
7186       EndDialog(hDlg, FALSE);\r
7187       return TRUE;\r
7188     default:\r
7189       break;\r
7190     }\r
7191     break;\r
7192   }\r
7193   return FALSE;\r
7194 }\r
7195 \r
7196 VOID\r
7197 PopUpMoveDialog(char firstchar)\r
7198 {\r
7199     FARPROC lpProc;\r
7200     \r
7201     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7202         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7203         gameMode == AnalyzeMode || gameMode == EditGame || \r
7204         gameMode == EditPosition || gameMode == IcsExamining ||\r
7205         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7206         gameMode == Training) {\r
7207       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7208       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7209         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7210       FreeProcInstance(lpProc);\r
7211     }\r
7212 }\r
7213 \r
7214 /*---------------------------------------------------------------------------*\\r
7215  *\r
7216  * Type-in name dialog functions\r
7217  * \r
7218 \*---------------------------------------------------------------------------*/\r
7219 \r
7220 LRESULT CALLBACK\r
7221 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7222 {\r
7223   char move[MSG_SIZ];\r
7224   HWND hInput;\r
7225 \r
7226   switch (message) {\r
7227   case WM_INITDIALOG:\r
7228     move[0] = (char) lParam;\r
7229     move[1] = NULLCHAR;\r
7230     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7231     hInput = GetDlgItem(hDlg, OPT_Name);\r
7232     SetWindowText(hInput, move);\r
7233     SetFocus(hInput);\r
7234     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7235     return FALSE;\r
7236 \r
7237   case WM_COMMAND:\r
7238     switch (LOWORD(wParam)) {\r
7239     case IDOK:\r
7240       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7241       appData.userName = strdup(move);\r
7242 \r
7243       EndDialog(hDlg, TRUE);\r
7244       return TRUE;\r
7245     case IDCANCEL:\r
7246       EndDialog(hDlg, FALSE);\r
7247       return TRUE;\r
7248     default:\r
7249       break;\r
7250     }\r
7251     break;\r
7252   }\r
7253   return FALSE;\r
7254 }\r
7255 \r
7256 VOID\r
7257 PopUpNameDialog(char firstchar)\r
7258 {\r
7259     FARPROC lpProc;\r
7260     \r
7261       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7262       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7263         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7264       FreeProcInstance(lpProc);\r
7265 }\r
7266 \r
7267 /*---------------------------------------------------------------------------*\\r
7268  *\r
7269  *  Error dialogs\r
7270  * \r
7271 \*---------------------------------------------------------------------------*/\r
7272 \r
7273 /* Nonmodal error box */\r
7274 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7275                              WPARAM wParam, LPARAM lParam);\r
7276 \r
7277 VOID\r
7278 ErrorPopUp(char *title, char *content)\r
7279 {\r
7280   FARPROC lpProc;\r
7281   char *p, *q;\r
7282   BOOLEAN modal = hwndMain == NULL;\r
7283 \r
7284   p = content;\r
7285   q = errorMessage;\r
7286   while (*p) {\r
7287     if (*p == '\n') {\r
7288       if (modal) {\r
7289         *q++ = ' ';\r
7290         p++;\r
7291       } else {\r
7292         *q++ = '\r';\r
7293         *q++ = *p++;\r
7294       }\r
7295     } else {\r
7296       *q++ = *p++;\r
7297     }\r
7298   }\r
7299   *q = NULLCHAR;\r
7300   strncpy(errorTitle, title, sizeof(errorTitle));\r
7301   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7302   \r
7303   if (modal) {\r
7304     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7305   } else {\r
7306     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7307     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7308                  hwndMain, (DLGPROC)lpProc);\r
7309     FreeProcInstance(lpProc);\r
7310   }\r
7311 }\r
7312 \r
7313 VOID\r
7314 ErrorPopDown()\r
7315 {\r
7316   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7317   if (errorDialog == NULL) return;\r
7318   DestroyWindow(errorDialog);\r
7319   errorDialog = NULL;\r
7320 }\r
7321 \r
7322 LRESULT CALLBACK\r
7323 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7324 {\r
7325   HANDLE hwndText;\r
7326   RECT rChild;\r
7327 \r
7328   switch (message) {\r
7329   case WM_INITDIALOG:\r
7330     GetWindowRect(hDlg, &rChild);\r
7331 \r
7332     /*\r
7333     SetWindowPos(hDlg, NULL, rChild.left,\r
7334       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7335       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7336     */\r
7337 \r
7338     /* \r
7339         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7340         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7341         and it doesn't work when you resize the dialog.\r
7342         For now, just give it a default position.\r
7343     */\r
7344     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7345 \r
7346     errorDialog = hDlg;\r
7347     SetWindowText(hDlg, errorTitle);\r
7348     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7349     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7350     return FALSE;\r
7351 \r
7352   case WM_COMMAND:\r
7353     switch (LOWORD(wParam)) {\r
7354     case IDOK:\r
7355     case IDCANCEL:\r
7356       if (errorDialog == hDlg) errorDialog = NULL;\r
7357       DestroyWindow(hDlg);\r
7358       return TRUE;\r
7359 \r
7360     default:\r
7361       break;\r
7362     }\r
7363     break;\r
7364   }\r
7365   return FALSE;\r
7366 }\r
7367 \r
7368 #ifdef GOTHIC\r
7369 HWND gothicDialog = NULL;\r
7370 \r
7371 LRESULT CALLBACK\r
7372 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   HANDLE hwndText;\r
7375   RECT rChild;\r
7376   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7377 \r
7378   switch (message) {\r
7379   case WM_INITDIALOG:\r
7380     GetWindowRect(hDlg, &rChild);\r
7381 \r
7382     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7383                                                              SWP_NOZORDER);\r
7384 \r
7385     /* \r
7386         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7387         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7388         and it doesn't work when you resize the dialog.\r
7389         For now, just give it a default position.\r
7390     */\r
7391     gothicDialog = hDlg;\r
7392     SetWindowText(hDlg, errorTitle);\r
7393     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7394     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7395     return FALSE;\r
7396 \r
7397   case WM_COMMAND:\r
7398     switch (LOWORD(wParam)) {\r
7399     case IDOK:\r
7400     case IDCANCEL:\r
7401       if (errorDialog == hDlg) errorDialog = NULL;\r
7402       DestroyWindow(hDlg);\r
7403       return TRUE;\r
7404 \r
7405     default:\r
7406       break;\r
7407     }\r
7408     break;\r
7409   }\r
7410   return FALSE;\r
7411 }\r
7412 \r
7413 VOID\r
7414 GothicPopUp(char *title, VariantClass variant)\r
7415 {\r
7416   FARPROC lpProc;\r
7417   static char *lastTitle;\r
7418 \r
7419   strncpy(errorTitle, title, sizeof(errorTitle));\r
7420   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7421 \r
7422   if(lastTitle != title && gothicDialog != NULL) {\r
7423     DestroyWindow(gothicDialog);\r
7424     gothicDialog = NULL;\r
7425   }\r
7426   if(variant != VariantNormal && gothicDialog == NULL) {\r
7427     title = lastTitle;\r
7428     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7429     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7430                  hwndMain, (DLGPROC)lpProc);\r
7431     FreeProcInstance(lpProc);\r
7432   }\r
7433 }\r
7434 #endif\r
7435 \r
7436 /*---------------------------------------------------------------------------*\\r
7437  *\r
7438  *  Ics Interaction console functions\r
7439  *\r
7440 \*---------------------------------------------------------------------------*/\r
7441 \r
7442 #define HISTORY_SIZE 64\r
7443 static char *history[HISTORY_SIZE];\r
7444 int histIn = 0, histP = 0;\r
7445 \r
7446 VOID\r
7447 SaveInHistory(char *cmd)\r
7448 {\r
7449   if (history[histIn] != NULL) {\r
7450     free(history[histIn]);\r
7451     history[histIn] = NULL;\r
7452   }\r
7453   if (*cmd == NULLCHAR) return;\r
7454   history[histIn] = StrSave(cmd);\r
7455   histIn = (histIn + 1) % HISTORY_SIZE;\r
7456   if (history[histIn] != NULL) {\r
7457     free(history[histIn]);\r
7458     history[histIn] = NULL;\r
7459   }\r
7460   histP = histIn;\r
7461 }\r
7462 \r
7463 char *\r
7464 PrevInHistory(char *cmd)\r
7465 {\r
7466   int newhp;\r
7467   if (histP == histIn) {\r
7468     if (history[histIn] != NULL) free(history[histIn]);\r
7469     history[histIn] = StrSave(cmd);\r
7470   }\r
7471   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7472   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7473   histP = newhp;\r
7474   return history[histP];\r
7475 }\r
7476 \r
7477 char *\r
7478 NextInHistory()\r
7479 {\r
7480   if (histP == histIn) return NULL;\r
7481   histP = (histP + 1) % HISTORY_SIZE;\r
7482   return history[histP];\r
7483 }\r
7484 \r
7485 typedef struct {\r
7486   char *item;\r
7487   char *command;\r
7488   BOOLEAN getname;\r
7489   BOOLEAN immediate;\r
7490 } IcsTextMenuEntry;\r
7491 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7492 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7493 \r
7494 void\r
7495 ParseIcsTextMenu(char *icsTextMenuString)\r
7496 {\r
7497 //  int flags = 0;\r
7498   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7499   char *p = icsTextMenuString;\r
7500   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7501     free(e->item);\r
7502     e->item = NULL;\r
7503     if (e->command != NULL) {\r
7504       free(e->command);\r
7505       e->command = NULL;\r
7506     }\r
7507     e++;\r
7508   }\r
7509   e = icsTextMenuEntry;\r
7510   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7511     if (*p == ';' || *p == '\n') {\r
7512       e->item = strdup("-");\r
7513       e->command = NULL;\r
7514       p++;\r
7515     } else if (*p == '-') {\r
7516       e->item = strdup("-");\r
7517       e->command = NULL;\r
7518       p++;\r
7519       if (*p) p++;\r
7520     } else {\r
7521       char *q, *r, *s, *t;\r
7522       char c;\r
7523       q = strchr(p, ',');\r
7524       if (q == NULL) break;\r
7525       *q = NULLCHAR;\r
7526       r = strchr(q + 1, ',');\r
7527       if (r == NULL) break;\r
7528       *r = NULLCHAR;\r
7529       s = strchr(r + 1, ',');\r
7530       if (s == NULL) break;\r
7531       *s = NULLCHAR;\r
7532       c = ';';\r
7533       t = strchr(s + 1, c);\r
7534       if (t == NULL) {\r
7535         c = '\n';\r
7536         t = strchr(s + 1, c);\r
7537       }\r
7538       if (t != NULL) *t = NULLCHAR;\r
7539       e->item = strdup(p);\r
7540       e->command = strdup(q + 1);\r
7541       e->getname = *(r + 1) != '0';\r
7542       e->immediate = *(s + 1) != '0';\r
7543       *q = ',';\r
7544       *r = ',';\r
7545       *s = ',';\r
7546       if (t == NULL) break;\r
7547       *t = c;\r
7548       p = t + 1;\r
7549     }\r
7550     e++;\r
7551   } \r
7552 }\r
7553 \r
7554 HMENU\r
7555 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7556 {\r
7557   HMENU hmenu, h;\r
7558   int i = 0;\r
7559   hmenu = LoadMenu(hInst, "TextMenu");\r
7560   h = GetSubMenu(hmenu, 0);\r
7561   while (e->item) {\r
7562     if (strcmp(e->item, "-") == 0) {\r
7563       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7564     } else {\r
7565       if (e->item[0] == '|') {\r
7566         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7567                    IDM_CommandX + i, &e->item[1]);\r
7568       } else {\r
7569         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7570       }\r
7571     }\r
7572     e++;\r
7573     i++;\r
7574   } \r
7575   return hmenu;\r
7576 }\r
7577 \r
7578 WNDPROC consoleTextWindowProc;\r
7579 \r
7580 void\r
7581 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7582 {\r
7583   char buf[MSG_SIZ], name[MSG_SIZ];\r
7584   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7585   CHARRANGE sel;\r
7586 \r
7587   if (!getname) {\r
7588     SetWindowText(hInput, command);\r
7589     if (immediate) {\r
7590       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7591     } else {\r
7592       sel.cpMin = 999999;\r
7593       sel.cpMax = 999999;\r
7594       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7595       SetFocus(hInput);\r
7596     }\r
7597     return;\r
7598   }    \r
7599   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7600   if (sel.cpMin == sel.cpMax) {\r
7601     /* Expand to surrounding word */\r
7602     TEXTRANGE tr;\r
7603     do {\r
7604       tr.chrg.cpMax = sel.cpMin;\r
7605       tr.chrg.cpMin = --sel.cpMin;\r
7606       if (sel.cpMin < 0) break;\r
7607       tr.lpstrText = name;\r
7608       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7609     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7610     sel.cpMin++;\r
7611 \r
7612     do {\r
7613       tr.chrg.cpMin = sel.cpMax;\r
7614       tr.chrg.cpMax = ++sel.cpMax;\r
7615       tr.lpstrText = name;\r
7616       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7617     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7618     sel.cpMax--;\r
7619 \r
7620     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7621       MessageBeep(MB_ICONEXCLAMATION);\r
7622       return;\r
7623     }\r
7624     tr.chrg = sel;\r
7625     tr.lpstrText = name;\r
7626     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7627   } else {\r
7628     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7629       MessageBeep(MB_ICONEXCLAMATION);\r
7630       return;\r
7631     }\r
7632     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7633   }\r
7634   if (immediate) {\r
7635     sprintf(buf, "%s %s", command, name);\r
7636     SetWindowText(hInput, buf);\r
7637     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7638   } else {\r
7639     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7640     SetWindowText(hInput, buf);\r
7641     sel.cpMin = 999999;\r
7642     sel.cpMax = 999999;\r
7643     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7644     SetFocus(hInput);\r
7645   }\r
7646 }\r
7647 \r
7648 LRESULT CALLBACK \r
7649 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7650 {\r
7651   HWND hInput;\r
7652   CHARRANGE sel;\r
7653 \r
7654   switch (message) {\r
7655   case WM_KEYDOWN:\r
7656     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7657     switch (wParam) {\r
7658     case VK_PRIOR:\r
7659       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7660       return 0;\r
7661     case VK_NEXT:\r
7662       sel.cpMin = 999999;\r
7663       sel.cpMax = 999999;\r
7664       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7665       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7666       return 0;\r
7667     }\r
7668     break;\r
7669   case WM_CHAR:\r
7670     if (wParam == '\t') {\r
7671       if (GetKeyState(VK_SHIFT) < 0) {\r
7672         /* shifted */\r
7673         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7674         if (buttonDesc[0].hwnd) {\r
7675           SetFocus(buttonDesc[0].hwnd);\r
7676         } else {\r
7677           SetFocus(hwndMain);\r
7678         }\r
7679       } else {\r
7680         /* unshifted */\r
7681         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7682       }\r
7683     } else {\r
7684       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7685       SetFocus(hInput);\r
7686       SendMessage(hInput, message, wParam, lParam);\r
7687     }\r
7688     return 0;\r
7689   case WM_PASTE:\r
7690     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7691     SetFocus(hInput);\r
7692     return SendMessage(hInput, message, wParam, lParam);\r
7693   case WM_MBUTTONDOWN:\r
7694     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7695   case WM_RBUTTONDOWN:\r
7696     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7697       /* Move selection here if it was empty */\r
7698       POINT pt;\r
7699       pt.x = LOWORD(lParam);\r
7700       pt.y = HIWORD(lParam);\r
7701       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7702       if (sel.cpMin == sel.cpMax) {\r
7703         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7704         sel.cpMax = sel.cpMin;\r
7705         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7706       }\r
7707       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7708     }\r
7709     return 0;\r
7710   case WM_RBUTTONUP:\r
7711     if (GetKeyState(VK_SHIFT) & ~1) {\r
7712       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7713         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7714     } else {\r
7715       POINT pt;\r
7716       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7717       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7718       if (sel.cpMin == sel.cpMax) {\r
7719         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7720         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7721       }\r
7722       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7723         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7724       }\r
7725       pt.x = LOWORD(lParam);\r
7726       pt.y = HIWORD(lParam);\r
7727       MenuPopup(hwnd, pt, hmenu, -1);\r
7728     }\r
7729     return 0;\r
7730   case WM_COMMAND:\r
7731     switch (LOWORD(wParam)) {\r
7732     case IDM_QuickPaste:\r
7733       {\r
7734         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7735         if (sel.cpMin == sel.cpMax) {\r
7736           MessageBeep(MB_ICONEXCLAMATION);\r
7737           return 0;\r
7738         }\r
7739         SendMessage(hwnd, WM_COPY, 0, 0);\r
7740         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7741         SendMessage(hInput, WM_PASTE, 0, 0);\r
7742         SetFocus(hInput);\r
7743         return 0;\r
7744       }\r
7745     case IDM_Cut:\r
7746       SendMessage(hwnd, WM_CUT, 0, 0);\r
7747       return 0;\r
7748     case IDM_Paste:\r
7749       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7750       return 0;\r
7751     case IDM_Copy:\r
7752       SendMessage(hwnd, WM_COPY, 0, 0);\r
7753       return 0;\r
7754     default:\r
7755       {\r
7756         int i = LOWORD(wParam) - IDM_CommandX;\r
7757         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7758             icsTextMenuEntry[i].command != NULL) {\r
7759           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7760                    icsTextMenuEntry[i].getname,\r
7761                    icsTextMenuEntry[i].immediate);\r
7762           return 0;\r
7763         }\r
7764       }\r
7765       break;\r
7766     }\r
7767     break;\r
7768   }\r
7769   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7770 }\r
7771 \r
7772 WNDPROC consoleInputWindowProc;\r
7773 \r
7774 LRESULT CALLBACK\r
7775 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7776 {\r
7777   char buf[MSG_SIZ];\r
7778   char *p;\r
7779   static BOOL sendNextChar = FALSE;\r
7780   static BOOL quoteNextChar = FALSE;\r
7781   InputSource *is = consoleInputSource;\r
7782   CHARFORMAT cf;\r
7783   CHARRANGE sel;\r
7784 \r
7785   switch (message) {\r
7786   case WM_CHAR:\r
7787     if (!appData.localLineEditing || sendNextChar) {\r
7788       is->buf[0] = (CHAR) wParam;\r
7789       is->count = 1;\r
7790       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7791       sendNextChar = FALSE;\r
7792       return 0;\r
7793     }\r
7794     if (quoteNextChar) {\r
7795       buf[0] = (char) wParam;\r
7796       buf[1] = NULLCHAR;\r
7797       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7798       quoteNextChar = FALSE;\r
7799       return 0;\r
7800     }\r
7801     switch (wParam) {\r
7802     case '\r':   /* Enter key */\r
7803       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7804       if (consoleEcho) SaveInHistory(is->buf);\r
7805       is->buf[is->count++] = '\n';\r
7806       is->buf[is->count] = NULLCHAR;\r
7807       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7808       if (consoleEcho) {\r
7809         ConsoleOutput(is->buf, is->count, TRUE);\r
7810       } else if (appData.localLineEditing) {\r
7811         ConsoleOutput("\n", 1, TRUE);\r
7812       }\r
7813       /* fall thru */\r
7814     case '\033': /* Escape key */\r
7815       SetWindowText(hwnd, "");\r
7816       cf.cbSize = sizeof(CHARFORMAT);\r
7817       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7818       if (consoleEcho) {\r
7819         cf.crTextColor = textAttribs[ColorNormal].color;\r
7820       } else {\r
7821         cf.crTextColor = COLOR_ECHOOFF;\r
7822       }\r
7823       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7824       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7825       return 0;\r
7826     case '\t':   /* Tab key */\r
7827       if (GetKeyState(VK_SHIFT) < 0) {\r
7828         /* shifted */\r
7829         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7830       } else {\r
7831         /* unshifted */\r
7832         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7833         if (buttonDesc[0].hwnd) {\r
7834           SetFocus(buttonDesc[0].hwnd);\r
7835         } else {\r
7836           SetFocus(hwndMain);\r
7837         }\r
7838       }\r
7839       return 0;\r
7840     case '\023': /* Ctrl+S */\r
7841       sendNextChar = TRUE;\r
7842       return 0;\r
7843     case '\021': /* Ctrl+Q */\r
7844       quoteNextChar = TRUE;\r
7845       return 0;\r
7846     default:\r
7847       break;\r
7848     }\r
7849     break;\r
7850   case WM_KEYDOWN:\r
7851     switch (wParam) {\r
7852     case VK_UP:\r
7853       GetWindowText(hwnd, buf, MSG_SIZ);\r
7854       p = PrevInHistory(buf);\r
7855       if (p != NULL) {\r
7856         SetWindowText(hwnd, p);\r
7857         sel.cpMin = 999999;\r
7858         sel.cpMax = 999999;\r
7859         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7860         return 0;\r
7861       }\r
7862       break;\r
7863     case VK_DOWN:\r
7864       p = NextInHistory();\r
7865       if (p != NULL) {\r
7866         SetWindowText(hwnd, p);\r
7867         sel.cpMin = 999999;\r
7868         sel.cpMax = 999999;\r
7869         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7870         return 0;\r
7871       }\r
7872       break;\r
7873     case VK_HOME:\r
7874     case VK_END:\r
7875       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7876       /* fall thru */\r
7877     case VK_PRIOR:\r
7878     case VK_NEXT:\r
7879       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7880       return 0;\r
7881     }\r
7882     break;\r
7883   case WM_MBUTTONDOWN:\r
7884     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7885       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7886     break;\r
7887   case WM_RBUTTONUP:\r
7888     if (GetKeyState(VK_SHIFT) & ~1) {\r
7889       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7890         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7891     } else {\r
7892       POINT pt;\r
7893       HMENU hmenu;\r
7894       hmenu = LoadMenu(hInst, "InputMenu");\r
7895       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7896       if (sel.cpMin == sel.cpMax) {\r
7897         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7898         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7899       }\r
7900       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7901         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7902       }\r
7903       pt.x = LOWORD(lParam);\r
7904       pt.y = HIWORD(lParam);\r
7905       MenuPopup(hwnd, pt, hmenu, -1);\r
7906     }\r
7907     return 0;\r
7908   case WM_COMMAND:\r
7909     switch (LOWORD(wParam)) { \r
7910     case IDM_Undo:\r
7911       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7912       return 0;\r
7913     case IDM_SelectAll:\r
7914       sel.cpMin = 0;\r
7915       sel.cpMax = -1; /*999999?*/\r
7916       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7917       return 0;\r
7918     case IDM_Cut:\r
7919       SendMessage(hwnd, WM_CUT, 0, 0);\r
7920       return 0;\r
7921     case IDM_Paste:\r
7922       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7923       return 0;\r
7924     case IDM_Copy:\r
7925       SendMessage(hwnd, WM_COPY, 0, 0);\r
7926       return 0;\r
7927     }\r
7928     break;\r
7929   }\r
7930   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7931 }\r
7932 \r
7933 #define CO_MAX  100000\r
7934 #define CO_TRIM   1000\r
7935 \r
7936 LRESULT CALLBACK\r
7937 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7938 {\r
7939   static SnapData sd;\r
7940   static HWND hText, hInput /*, hFocus*/;\r
7941 //  InputSource *is = consoleInputSource;\r
7942   RECT rect;\r
7943   static int sizeX, sizeY;\r
7944   int newSizeX, newSizeY;\r
7945   MINMAXINFO *mmi;\r
7946 \r
7947   switch (message) {\r
7948   case WM_INITDIALOG: /* message: initialize dialog box */\r
7949     hwndConsole = hDlg;\r
7950     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7951     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7952     SetFocus(hInput);\r
7953     consoleTextWindowProc = (WNDPROC)\r
7954       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7955     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7956     consoleInputWindowProc = (WNDPROC)\r
7957       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7958     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7959     Colorize(ColorNormal, TRUE);\r
7960     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7961     ChangedConsoleFont();\r
7962     GetClientRect(hDlg, &rect);\r
7963     sizeX = rect.right;\r
7964     sizeY = rect.bottom;\r
7965     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7966         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7967       WINDOWPLACEMENT wp;\r
7968       EnsureOnScreen(&consoleX, &consoleY);\r
7969       wp.length = sizeof(WINDOWPLACEMENT);\r
7970       wp.flags = 0;\r
7971       wp.showCmd = SW_SHOW;\r
7972       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7973       wp.rcNormalPosition.left = consoleX;\r
7974       wp.rcNormalPosition.right = consoleX + consoleW;\r
7975       wp.rcNormalPosition.top = consoleY;\r
7976       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7977       SetWindowPlacement(hDlg, &wp);\r
7978     }\r
7979 #if 0 \r
7980    // [HGM] Chessknight's change 2004-07-13\r
7981    else { /* Determine Defaults */\r
7982        WINDOWPLACEMENT wp;\r
7983        consoleX = winWidth + 1;\r
7984        consoleY = boardY;\r
7985        consoleW = screenWidth -  winWidth;\r
7986        consoleH = winHeight;\r
7987        EnsureOnScreen(&consoleX, &consoleY);\r
7988        wp.length = sizeof(WINDOWPLACEMENT);\r
7989        wp.flags = 0;\r
7990        wp.showCmd = SW_SHOW;\r
7991        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7992        wp.rcNormalPosition.left = consoleX;\r
7993        wp.rcNormalPosition.right = consoleX + consoleW;\r
7994        wp.rcNormalPosition.top = consoleY;\r
7995        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7996        SetWindowPlacement(hDlg, &wp);\r
7997     }\r
7998 #endif\r
7999     return FALSE;\r
8000 \r
8001   case WM_SETFOCUS:\r
8002     SetFocus(hInput);\r
8003     return 0;\r
8004 \r
8005   case WM_CLOSE:\r
8006     ExitEvent(0);\r
8007     /* not reached */\r
8008     break;\r
8009 \r
8010   case WM_SIZE:\r
8011     if (IsIconic(hDlg)) break;\r
8012     newSizeX = LOWORD(lParam);\r
8013     newSizeY = HIWORD(lParam);\r
8014     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8015       RECT rectText, rectInput;\r
8016       POINT pt;\r
8017       int newTextHeight, newTextWidth;\r
8018       GetWindowRect(hText, &rectText);\r
8019       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8020       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8021       if (newTextHeight < 0) {\r
8022         newSizeY += -newTextHeight;\r
8023         newTextHeight = 0;\r
8024       }\r
8025       SetWindowPos(hText, NULL, 0, 0,\r
8026         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8027       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8028       pt.x = rectInput.left;\r
8029       pt.y = rectInput.top + newSizeY - sizeY;\r
8030       ScreenToClient(hDlg, &pt);\r
8031       SetWindowPos(hInput, NULL, \r
8032         pt.x, pt.y, /* needs client coords */   \r
8033         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8034         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8035     }\r
8036     sizeX = newSizeX;\r
8037     sizeY = newSizeY;\r
8038     break;\r
8039 \r
8040   case WM_GETMINMAXINFO:\r
8041     /* Prevent resizing window too small */\r
8042     mmi = (MINMAXINFO *) lParam;\r
8043     mmi->ptMinTrackSize.x = 100;\r
8044     mmi->ptMinTrackSize.y = 100;\r
8045     break;\r
8046 \r
8047   /* [AS] Snapping */\r
8048   case WM_ENTERSIZEMOVE:\r
8049     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8050 \r
8051   case WM_SIZING:\r
8052     return OnSizing( &sd, hDlg, wParam, lParam );\r
8053 \r
8054   case WM_MOVING:\r
8055     return OnMoving( &sd, hDlg, wParam, lParam );\r
8056 \r
8057   case WM_EXITSIZEMOVE:\r
8058     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8059   }\r
8060 \r
8061   return DefWindowProc(hDlg, message, wParam, lParam);\r
8062 }\r
8063 \r
8064 \r
8065 VOID\r
8066 ConsoleCreate()\r
8067 {\r
8068   HWND hCons;\r
8069   if (hwndConsole) return;\r
8070   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8071   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8072 }\r
8073 \r
8074 \r
8075 VOID\r
8076 ConsoleOutput(char* data, int length, int forceVisible)\r
8077 {\r
8078   HWND hText;\r
8079   int trim, exlen;\r
8080   char *p, *q;\r
8081   char buf[CO_MAX+1];\r
8082   POINT pEnd;\r
8083   RECT rect;\r
8084   static int delayLF = 0;\r
8085   CHARRANGE savesel, sel;\r
8086 \r
8087   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8088   p = data;\r
8089   q = buf;\r
8090   if (delayLF) {\r
8091     *q++ = '\r';\r
8092     *q++ = '\n';\r
8093     delayLF = 0;\r
8094   }\r
8095   while (length--) {\r
8096     if (*p == '\n') {\r
8097       if (*++p) {\r
8098         *q++ = '\r';\r
8099         *q++ = '\n';\r
8100       } else {\r
8101         delayLF = 1;\r
8102       }\r
8103     } else if (*p == '\007') {\r
8104        MyPlaySound(&sounds[(int)SoundBell]);\r
8105        p++;\r
8106     } else {\r
8107       *q++ = *p++;\r
8108     }\r
8109   }\r
8110   *q = NULLCHAR;\r
8111   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8112   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8113   /* Save current selection */\r
8114   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8115   exlen = GetWindowTextLength(hText);\r
8116   /* Find out whether current end of text is visible */\r
8117   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8118   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8119   /* Trim existing text if it's too long */\r
8120   if (exlen + (q - buf) > CO_MAX) {\r
8121     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8122     sel.cpMin = 0;\r
8123     sel.cpMax = trim;\r
8124     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8125     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8126     exlen -= trim;\r
8127     savesel.cpMin -= trim;\r
8128     savesel.cpMax -= trim;\r
8129     if (exlen < 0) exlen = 0;\r
8130     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8131     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8132   }\r
8133   /* Append the new text */\r
8134   sel.cpMin = exlen;\r
8135   sel.cpMax = exlen;\r
8136   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8137   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8138   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8139   if (forceVisible || exlen == 0 ||\r
8140       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8141        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8142     /* Scroll to make new end of text visible if old end of text\r
8143        was visible or new text is an echo of user typein */\r
8144     sel.cpMin = 9999999;\r
8145     sel.cpMax = 9999999;\r
8146     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8147     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8148     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8149     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8150   }\r
8151   if (savesel.cpMax == exlen || forceVisible) {\r
8152     /* Move insert point to new end of text if it was at the old\r
8153        end of text or if the new text is an echo of user typein */\r
8154     sel.cpMin = 9999999;\r
8155     sel.cpMax = 9999999;\r
8156     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8157   } else {\r
8158     /* Restore previous selection */\r
8159     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8160   }\r
8161   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8162 }\r
8163 \r
8164 /*---------*/\r
8165 \r
8166 \r
8167 void\r
8168 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8169 {\r
8170   char buf[100];\r
8171   char *str;\r
8172   COLORREF oldFg, oldBg;\r
8173   HFONT oldFont;\r
8174   RECT rect;\r
8175 \r
8176   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8177 \r
8178   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8179   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8180   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8181 \r
8182   rect.left = x;\r
8183   rect.right = x + squareSize;\r
8184   rect.top  = y;\r
8185   rect.bottom = y + squareSize;\r
8186   str = buf;\r
8187 \r
8188   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8189                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8190              y, ETO_CLIPPED|ETO_OPAQUE,\r
8191              &rect, str, strlen(str), NULL);\r
8192 \r
8193   (void) SetTextColor(hdc, oldFg);\r
8194   (void) SetBkColor(hdc, oldBg);\r
8195   (void) SelectObject(hdc, oldFont);\r
8196 }\r
8197 \r
8198 void\r
8199 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8200               RECT *rect, char *color, char *flagFell)\r
8201 {\r
8202   char buf[100];\r
8203   char *str;\r
8204   COLORREF oldFg, oldBg;\r
8205   HFONT oldFont;\r
8206 \r
8207   if (appData.clockMode) {\r
8208     if (tinyLayout)\r
8209       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8210     else\r
8211       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8212     str = buf;\r
8213   } else {\r
8214     str = color;\r
8215   }\r
8216 \r
8217   if (highlight) {\r
8218     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8219     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8220   } else {\r
8221     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8222     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8223   }\r
8224   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8225 \r
8226   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8227              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8228              rect, str, strlen(str), NULL);\r
8229 \r
8230   (void) SetTextColor(hdc, oldFg);\r
8231   (void) SetBkColor(hdc, oldBg);\r
8232   (void) SelectObject(hdc, oldFont);\r
8233 }\r
8234 \r
8235 \r
8236 int\r
8237 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8238            OVERLAPPED *ovl)\r
8239 {\r
8240   int ok, err;\r
8241 \r
8242   /* [AS]  */\r
8243   if( count <= 0 ) {\r
8244     if (appData.debugMode) {\r
8245       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8246     }\r
8247 \r
8248     return ERROR_INVALID_USER_BUFFER;\r
8249   }\r
8250 \r
8251   ResetEvent(ovl->hEvent);\r
8252   ovl->Offset = ovl->OffsetHigh = 0;\r
8253   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8254   if (ok) {\r
8255     err = NO_ERROR;\r
8256   } else {\r
8257     err = GetLastError();\r
8258     if (err == ERROR_IO_PENDING) {\r
8259       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8260       if (ok)\r
8261         err = NO_ERROR;\r
8262       else\r
8263         err = GetLastError();\r
8264     }\r
8265   }\r
8266   return err;\r
8267 }\r
8268 \r
8269 int\r
8270 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8271             OVERLAPPED *ovl)\r
8272 {\r
8273   int ok, err;\r
8274 \r
8275   ResetEvent(ovl->hEvent);\r
8276   ovl->Offset = ovl->OffsetHigh = 0;\r
8277   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8278   if (ok) {\r
8279     err = NO_ERROR;\r
8280   } else {\r
8281     err = GetLastError();\r
8282     if (err == ERROR_IO_PENDING) {\r
8283       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8284       if (ok)\r
8285         err = NO_ERROR;\r
8286       else\r
8287         err = GetLastError();\r
8288     }\r
8289   }\r
8290   return err;\r
8291 }\r
8292 \r
8293 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8294 void CheckForInputBufferFull( InputSource * is )\r
8295 {\r
8296     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8297         /* Look for end of line */\r
8298         char * p = is->buf;\r
8299         \r
8300         while( p < is->next && *p != '\n' ) {\r
8301             p++;\r
8302         }\r
8303 \r
8304         if( p >= is->next ) {\r
8305             if (appData.debugMode) {\r
8306                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8307             }\r
8308 \r
8309             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8310             is->count = (DWORD) -1;\r
8311             is->next = is->buf;\r
8312         }\r
8313     }\r
8314 }\r
8315 \r
8316 DWORD\r
8317 InputThread(LPVOID arg)\r
8318 {\r
8319   InputSource *is;\r
8320   OVERLAPPED ovl;\r
8321 \r
8322   is = (InputSource *) arg;\r
8323   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8324   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8325   while (is->hThread != NULL) {\r
8326     is->error = DoReadFile(is->hFile, is->next,\r
8327                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8328                            &is->count, &ovl);\r
8329     if (is->error == NO_ERROR) {\r
8330       is->next += is->count;\r
8331     } else {\r
8332       if (is->error == ERROR_BROKEN_PIPE) {\r
8333         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8334         is->count = 0;\r
8335       } else {\r
8336         is->count = (DWORD) -1;\r
8337         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8338         break; \r
8339       }\r
8340     }\r
8341 \r
8342     CheckForInputBufferFull( is );\r
8343 \r
8344     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8345 \r
8346     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8347 \r
8348     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8349   }\r
8350 \r
8351   CloseHandle(ovl.hEvent);\r
8352   CloseHandle(is->hFile);\r
8353 \r
8354   if (appData.debugMode) {\r
8355     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8356   }\r
8357 \r
8358   return 0;\r
8359 }\r
8360 \r
8361 \r
8362 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8363 DWORD\r
8364 NonOvlInputThread(LPVOID arg)\r
8365 {\r
8366   InputSource *is;\r
8367   char *p, *q;\r
8368   int i;\r
8369   char prev;\r
8370 \r
8371   is = (InputSource *) arg;\r
8372   while (is->hThread != NULL) {\r
8373     is->error = ReadFile(is->hFile, is->next,\r
8374                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8375                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8376     if (is->error == NO_ERROR) {\r
8377       /* Change CRLF to LF */\r
8378       if (is->next > is->buf) {\r
8379         p = is->next - 1;\r
8380         i = is->count + 1;\r
8381       } else {\r
8382         p = is->next;\r
8383         i = is->count;\r
8384       }\r
8385       q = p;\r
8386       prev = NULLCHAR;\r
8387       while (i > 0) {\r
8388         if (prev == '\r' && *p == '\n') {\r
8389           *(q-1) = '\n';\r
8390           is->count--;\r
8391         } else { \r
8392           *q++ = *p;\r
8393         }\r
8394         prev = *p++;\r
8395         i--;\r
8396       }\r
8397       *q = NULLCHAR;\r
8398       is->next = q;\r
8399     } else {\r
8400       if (is->error == ERROR_BROKEN_PIPE) {\r
8401         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8402         is->count = 0; \r
8403       } else {\r
8404         is->count = (DWORD) -1;\r
8405       }\r
8406     }\r
8407 \r
8408     CheckForInputBufferFull( is );\r
8409 \r
8410     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8411 \r
8412     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8413 \r
8414     if (is->count < 0) break;  /* Quit on error */\r
8415   }\r
8416   CloseHandle(is->hFile);\r
8417   return 0;\r
8418 }\r
8419 \r
8420 DWORD\r
8421 SocketInputThread(LPVOID arg)\r
8422 {\r
8423   InputSource *is;\r
8424 \r
8425   is = (InputSource *) arg;\r
8426   while (is->hThread != NULL) {\r
8427     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8428     if ((int)is->count == SOCKET_ERROR) {\r
8429       is->count = (DWORD) -1;\r
8430       is->error = WSAGetLastError();\r
8431     } else {\r
8432       is->error = NO_ERROR;\r
8433       is->next += is->count;\r
8434       if (is->count == 0 && is->second == is) {\r
8435         /* End of file on stderr; quit with no message */\r
8436         break;\r
8437       }\r
8438     }\r
8439     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8440 \r
8441     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8442 \r
8443     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8444   }\r
8445   return 0;\r
8446 }\r
8447 \r
8448 VOID\r
8449 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8450 {\r
8451   InputSource *is;\r
8452 \r
8453   is = (InputSource *) lParam;\r
8454   if (is->lineByLine) {\r
8455     /* Feed in lines one by one */\r
8456     char *p = is->buf;\r
8457     char *q = p;\r
8458     while (q < is->next) {\r
8459       if (*q++ == '\n') {\r
8460         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8461         p = q;\r
8462       }\r
8463     }\r
8464     \r
8465     /* Move any partial line to the start of the buffer */\r
8466     q = is->buf;\r
8467     while (p < is->next) {\r
8468       *q++ = *p++;\r
8469     }\r
8470     is->next = q;\r
8471 \r
8472     if (is->error != NO_ERROR || is->count == 0) {\r
8473       /* Notify backend of the error.  Note: If there was a partial\r
8474          line at the end, it is not flushed through. */\r
8475       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8476     }\r
8477   } else {\r
8478     /* Feed in the whole chunk of input at once */\r
8479     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8480     is->next = is->buf;\r
8481   }\r
8482 }\r
8483 \r
8484 /*---------------------------------------------------------------------------*\\r
8485  *\r
8486  *  Menu enables. Used when setting various modes.\r
8487  *\r
8488 \*---------------------------------------------------------------------------*/\r
8489 \r
8490 typedef struct {\r
8491   int item;\r
8492   int flags;\r
8493 } Enables;\r
8494 \r
8495 VOID\r
8496 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8497 {\r
8498   while (enab->item > 0) {\r
8499     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8500     enab++;\r
8501   }\r
8502 }\r
8503 \r
8504 Enables gnuEnables[] = {\r
8505   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8515   { -1, -1 }\r
8516 };\r
8517 \r
8518 Enables icsEnables[] = {\r
8519   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8520   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8521   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8522   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8523   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8525   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8529   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8531   { -1, -1 }\r
8532 };\r
8533 \r
8534 #ifdef ZIPPY\r
8535 Enables zippyEnables[] = {\r
8536   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8537   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8538   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8539   { -1, -1 }\r
8540 };\r
8541 #endif\r
8542 \r
8543 Enables ncpEnables[] = {\r
8544   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8545   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8546   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8552   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8553   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8559   { -1, -1 }\r
8560 };\r
8561 \r
8562 Enables trainingOnEnables[] = {\r
8563   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8571   { -1, -1 }\r
8572 };\r
8573 \r
8574 Enables trainingOffEnables[] = {\r
8575   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8576   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8577   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8578   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8579   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8580   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8581   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8582   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8583   { -1, -1 }\r
8584 };\r
8585 \r
8586 /* These modify either ncpEnables or gnuEnables */\r
8587 Enables cmailEnables[] = {\r
8588   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8589   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8590   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8591   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8593   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8595   { -1, -1 }\r
8596 };\r
8597 \r
8598 Enables machineThinkingEnables[] = {\r
8599   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8614   { -1, -1 }\r
8615 };\r
8616 \r
8617 Enables userThinkingEnables[] = {\r
8618   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8619   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8620   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8621   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8622   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8623   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8624   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8633   { -1, -1 }\r
8634 };\r
8635 \r
8636 /*---------------------------------------------------------------------------*\\r
8637  *\r
8638  *  Front-end interface functions exported by XBoard.\r
8639  *  Functions appear in same order as prototypes in frontend.h.\r
8640  * \r
8641 \*---------------------------------------------------------------------------*/\r
8642 VOID\r
8643 ModeHighlight()\r
8644 {\r
8645   static UINT prevChecked = 0;\r
8646   static int prevPausing = 0;\r
8647   UINT nowChecked;\r
8648 \r
8649   if (pausing != prevPausing) {\r
8650     prevPausing = pausing;\r
8651     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8652                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8653     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8654   }\r
8655 \r
8656   switch (gameMode) {\r
8657   case BeginningOfGame:\r
8658     if (appData.icsActive)\r
8659       nowChecked = IDM_IcsClient;\r
8660     else if (appData.noChessProgram)\r
8661       nowChecked = IDM_EditGame;\r
8662     else\r
8663       nowChecked = IDM_MachineBlack;\r
8664     break;\r
8665   case MachinePlaysBlack:\r
8666     nowChecked = IDM_MachineBlack;\r
8667     break;\r
8668   case MachinePlaysWhite:\r
8669     nowChecked = IDM_MachineWhite;\r
8670     break;\r
8671   case TwoMachinesPlay:\r
8672     nowChecked = IDM_TwoMachines;\r
8673     break;\r
8674   case AnalyzeMode:\r
8675     nowChecked = IDM_AnalysisMode;\r
8676     break;\r
8677   case AnalyzeFile:\r
8678     nowChecked = IDM_AnalyzeFile;\r
8679     break;\r
8680   case EditGame:\r
8681     nowChecked = IDM_EditGame;\r
8682     break;\r
8683   case PlayFromGameFile:\r
8684     nowChecked = IDM_LoadGame;\r
8685     break;\r
8686   case EditPosition:\r
8687     nowChecked = IDM_EditPosition;\r
8688     break;\r
8689   case Training:\r
8690     nowChecked = IDM_Training;\r
8691     break;\r
8692   case IcsPlayingWhite:\r
8693   case IcsPlayingBlack:\r
8694   case IcsObserving:\r
8695   case IcsIdle:\r
8696     nowChecked = IDM_IcsClient;\r
8697     break;\r
8698   default:\r
8699   case EndOfGame:\r
8700     nowChecked = 0;\r
8701     break;\r
8702   }\r
8703   if (prevChecked != 0)\r
8704     (void) CheckMenuItem(GetMenu(hwndMain),\r
8705                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8706   if (nowChecked != 0)\r
8707     (void) CheckMenuItem(GetMenu(hwndMain),\r
8708                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8709 \r
8710   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8711     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8712                           MF_BYCOMMAND|MF_ENABLED);\r
8713   } else {\r
8714     (void) EnableMenuItem(GetMenu(hwndMain), \r
8715                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8716   }\r
8717 \r
8718   prevChecked = nowChecked;\r
8719 \r
8720   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8721   if (appData.icsActive) {\r
8722        if (appData.icsEngineAnalyze) {\r
8723                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8724                        MF_BYCOMMAND|MF_CHECKED);\r
8725        } else {\r
8726                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8727                        MF_BYCOMMAND|MF_UNCHECKED);\r
8728        }\r
8729   }\r
8730 }\r
8731 \r
8732 VOID\r
8733 SetICSMode()\r
8734 {\r
8735   HMENU hmenu = GetMenu(hwndMain);\r
8736   SetMenuEnables(hmenu, icsEnables);\r
8737   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8738     MF_BYPOSITION|MF_ENABLED);\r
8739 #ifdef ZIPPY\r
8740   if (appData.zippyPlay) {\r
8741     SetMenuEnables(hmenu, zippyEnables);\r
8742     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8743          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8744           MF_BYCOMMAND|MF_ENABLED);\r
8745   }\r
8746 #endif\r
8747 }\r
8748 \r
8749 VOID\r
8750 SetGNUMode()\r
8751 {\r
8752   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8753 }\r
8754 \r
8755 VOID\r
8756 SetNCPMode()\r
8757 {\r
8758   HMENU hmenu = GetMenu(hwndMain);\r
8759   SetMenuEnables(hmenu, ncpEnables);\r
8760   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8761     MF_BYPOSITION|MF_GRAYED);\r
8762     DrawMenuBar(hwndMain);\r
8763 }\r
8764 \r
8765 VOID\r
8766 SetCmailMode()\r
8767 {\r
8768   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8769 }\r
8770 \r
8771 VOID \r
8772 SetTrainingModeOn()\r
8773 {\r
8774   int i;\r
8775   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8776   for (i = 0; i < N_BUTTONS; i++) {\r
8777     if (buttonDesc[i].hwnd != NULL)\r
8778       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8779   }\r
8780   CommentPopDown();\r
8781 }\r
8782 \r
8783 VOID SetTrainingModeOff()\r
8784 {\r
8785   int i;\r
8786   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8787   for (i = 0; i < N_BUTTONS; i++) {\r
8788     if (buttonDesc[i].hwnd != NULL)\r
8789       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8790   }\r
8791 }\r
8792 \r
8793 \r
8794 VOID\r
8795 SetUserThinkingEnables()\r
8796 {\r
8797   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8798 }\r
8799 \r
8800 VOID\r
8801 SetMachineThinkingEnables()\r
8802 {\r
8803   HMENU hMenu = GetMenu(hwndMain);\r
8804   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8805 \r
8806   SetMenuEnables(hMenu, machineThinkingEnables);\r
8807 \r
8808   if (gameMode == MachinePlaysBlack) {\r
8809     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8810   } else if (gameMode == MachinePlaysWhite) {\r
8811     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8812   } else if (gameMode == TwoMachinesPlay) {\r
8813     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8814   }\r
8815 }\r
8816 \r
8817 \r
8818 VOID\r
8819 DisplayTitle(char *str)\r
8820 {\r
8821   char title[MSG_SIZ], *host;\r
8822   if (str[0] != NULLCHAR) {\r
8823     strcpy(title, str);\r
8824   } else if (appData.icsActive) {\r
8825     if (appData.icsCommPort[0] != NULLCHAR)\r
8826       host = "ICS";\r
8827     else \r
8828       host = appData.icsHost;\r
8829     sprintf(title, "%s: %s", szTitle, host);\r
8830   } else if (appData.noChessProgram) {\r
8831     strcpy(title, szTitle);\r
8832   } else {\r
8833     strcpy(title, szTitle);\r
8834     strcat(title, ": ");\r
8835     strcat(title, first.tidy);\r
8836   }\r
8837   SetWindowText(hwndMain, title);\r
8838 }\r
8839 \r
8840 \r
8841 VOID\r
8842 DisplayMessage(char *str1, char *str2)\r
8843 {\r
8844   HDC hdc;\r
8845   HFONT oldFont;\r
8846   int remain = MESSAGE_TEXT_MAX - 1;\r
8847   int len;\r
8848 \r
8849   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8850   messageText[0] = NULLCHAR;\r
8851   if (*str1) {\r
8852     len = strlen(str1);\r
8853     if (len > remain) len = remain;\r
8854     strncpy(messageText, str1, len);\r
8855     messageText[len] = NULLCHAR;\r
8856     remain -= len;\r
8857   }\r
8858   if (*str2 && remain >= 2) {\r
8859     if (*str1) {\r
8860       strcat(messageText, "  ");\r
8861       remain -= 2;\r
8862     }\r
8863     len = strlen(str2);\r
8864     if (len > remain) len = remain;\r
8865     strncat(messageText, str2, len);\r
8866   }\r
8867   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8868 \r
8869   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8870   hdc = GetDC(hwndMain);\r
8871   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8872   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8873              &messageRect, messageText, strlen(messageText), NULL);\r
8874   (void) SelectObject(hdc, oldFont);\r
8875   (void) ReleaseDC(hwndMain, hdc);\r
8876 }\r
8877 \r
8878 VOID\r
8879 DisplayError(char *str, int error)\r
8880 {\r
8881   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8882   int len;\r
8883 \r
8884   if (error == 0) {\r
8885     strcpy(buf, str);\r
8886   } else {\r
8887     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8888                         NULL, error, LANG_NEUTRAL,\r
8889                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8890     if (len > 0) {\r
8891       sprintf(buf, "%s:\n%s", str, buf2);\r
8892     } else {\r
8893       ErrorMap *em = errmap;\r
8894       while (em->err != 0 && em->err != error) em++;\r
8895       if (em->err != 0) {\r
8896         sprintf(buf, "%s:\n%s", str, em->msg);\r
8897       } else {\r
8898         sprintf(buf, "%s:\nError code %d", str, error);\r
8899       }\r
8900     }\r
8901   }\r
8902   \r
8903   ErrorPopUp("Error", buf);\r
8904 }\r
8905 \r
8906 \r
8907 VOID\r
8908 DisplayMoveError(char *str)\r
8909 {\r
8910   fromX = fromY = -1;\r
8911   ClearHighlights();\r
8912   DrawPosition(FALSE, NULL);\r
8913   if (appData.popupMoveErrors) {\r
8914     ErrorPopUp("Error", str);\r
8915   } else {\r
8916     DisplayMessage(str, "");\r
8917     moveErrorMessageUp = TRUE;\r
8918   }\r
8919 }\r
8920 \r
8921 VOID\r
8922 DisplayFatalError(char *str, int error, int exitStatus)\r
8923 {\r
8924   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8925   int len;\r
8926   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8927 \r
8928   if (error != 0) {\r
8929     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8930                         NULL, error, LANG_NEUTRAL,\r
8931                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8932     if (len > 0) {\r
8933       sprintf(buf, "%s:\n%s", str, buf2);\r
8934     } else {\r
8935       ErrorMap *em = errmap;\r
8936       while (em->err != 0 && em->err != error) em++;\r
8937       if (em->err != 0) {\r
8938         sprintf(buf, "%s:\n%s", str, em->msg);\r
8939       } else {\r
8940         sprintf(buf, "%s:\nError code %d", str, error);\r
8941       }\r
8942     }\r
8943     str = buf;\r
8944   }\r
8945   if (appData.debugMode) {\r
8946     fprintf(debugFP, "%s: %s\n", label, str);\r
8947   }\r
8948   if (appData.popupExitMessage) {\r
8949     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8950                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8951   }\r
8952   ExitEvent(exitStatus);\r
8953 }\r
8954 \r
8955 \r
8956 VOID\r
8957 DisplayInformation(char *str)\r
8958 {\r
8959   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8960 }\r
8961 \r
8962 \r
8963 VOID\r
8964 DisplayNote(char *str)\r
8965 {\r
8966   ErrorPopUp("Note", str);\r
8967 }\r
8968 \r
8969 \r
8970 typedef struct {\r
8971   char *title, *question, *replyPrefix;\r
8972   ProcRef pr;\r
8973 } QuestionParams;\r
8974 \r
8975 LRESULT CALLBACK\r
8976 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8977 {\r
8978   static QuestionParams *qp;\r
8979   char reply[MSG_SIZ];\r
8980   int len, err;\r
8981 \r
8982   switch (message) {\r
8983   case WM_INITDIALOG:\r
8984     qp = (QuestionParams *) lParam;\r
8985     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8986     SetWindowText(hDlg, qp->title);\r
8987     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8988     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8989     return FALSE;\r
8990 \r
8991   case WM_COMMAND:\r
8992     switch (LOWORD(wParam)) {\r
8993     case IDOK:\r
8994       strcpy(reply, qp->replyPrefix);\r
8995       if (*reply) strcat(reply, " ");\r
8996       len = strlen(reply);\r
8997       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8998       strcat(reply, "\n");\r
8999       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9000       EndDialog(hDlg, TRUE);\r
9001       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9002       return TRUE;\r
9003     case IDCANCEL:\r
9004       EndDialog(hDlg, FALSE);\r
9005       return TRUE;\r
9006     default:\r
9007       break;\r
9008     }\r
9009     break;\r
9010   }\r
9011   return FALSE;\r
9012 }\r
9013 \r
9014 VOID\r
9015 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9016 {\r
9017     QuestionParams qp;\r
9018     FARPROC lpProc;\r
9019     \r
9020     qp.title = title;\r
9021     qp.question = question;\r
9022     qp.replyPrefix = replyPrefix;\r
9023     qp.pr = pr;\r
9024     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9025     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9026       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9027     FreeProcInstance(lpProc);\r
9028 }\r
9029 \r
9030 /* [AS] Pick FRC position */\r
9031 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9032 {\r
9033     static int * lpIndexFRC;\r
9034     BOOL index_is_ok;\r
9035     char buf[16];\r
9036 \r
9037     switch( message )\r
9038     {\r
9039     case WM_INITDIALOG:\r
9040         lpIndexFRC = (int *) lParam;\r
9041 \r
9042         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9043 \r
9044         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9045         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9046         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9047         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9048 \r
9049         break;\r
9050 \r
9051     case WM_COMMAND:\r
9052         switch( LOWORD(wParam) ) {\r
9053         case IDOK:\r
9054             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9055             EndDialog( hDlg, 0 );\r
9056             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9057             return TRUE;\r
9058         case IDCANCEL:\r
9059             EndDialog( hDlg, 1 );   \r
9060             return TRUE;\r
9061         case IDC_NFG_Edit:\r
9062             if( HIWORD(wParam) == EN_CHANGE ) {\r
9063                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9064 \r
9065                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9066             }\r
9067             return TRUE;\r
9068         case IDC_NFG_Random:\r
9069             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9070             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9071             return TRUE;\r
9072         }\r
9073 \r
9074         break;\r
9075     }\r
9076 \r
9077     return FALSE;\r
9078 }\r
9079 \r
9080 int NewGameFRC()\r
9081 {\r
9082     int result;\r
9083     int index = appData.defaultFrcPosition;\r
9084     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9085 \r
9086     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9087 \r
9088     if( result == 0 ) {\r
9089         appData.defaultFrcPosition = index;\r
9090     }\r
9091 \r
9092     return result;\r
9093 }\r
9094 \r
9095 /* [AS] Game list options */\r
9096 typedef struct {\r
9097     char id;\r
9098     char * name;\r
9099 } GLT_Item;\r
9100 \r
9101 static GLT_Item GLT_ItemInfo[] = {\r
9102     { GLT_EVENT,      "Event" },\r
9103     { GLT_SITE,       "Site" },\r
9104     { GLT_DATE,       "Date" },\r
9105     { GLT_ROUND,      "Round" },\r
9106     { GLT_PLAYERS,    "Players" },\r
9107     { GLT_RESULT,     "Result" },\r
9108     { GLT_WHITE_ELO,  "White Rating" },\r
9109     { GLT_BLACK_ELO,  "Black Rating" },\r
9110     { GLT_TIME_CONTROL,"Time Control" },\r
9111     { GLT_VARIANT,    "Variant" },\r
9112     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9113     { 0, 0 }\r
9114 };\r
9115 \r
9116 const char * GLT_FindItem( char id )\r
9117 {\r
9118     const char * result = 0;\r
9119 \r
9120     GLT_Item * list = GLT_ItemInfo;\r
9121 \r
9122     while( list->id != 0 ) {\r
9123         if( list->id == id ) {\r
9124             result = list->name;\r
9125             break;\r
9126         }\r
9127 \r
9128         list++;\r
9129     }\r
9130 \r
9131     return result;\r
9132 }\r
9133 \r
9134 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9135 {\r
9136     const char * name = GLT_FindItem( id );\r
9137 \r
9138     if( name != 0 ) {\r
9139         if( index >= 0 ) {\r
9140             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9141         }\r
9142         else {\r
9143             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9144         }\r
9145     }\r
9146 }\r
9147 \r
9148 void GLT_TagsToList( HWND hDlg, char * tags )\r
9149 {\r
9150     char * pc = tags;\r
9151 \r
9152     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9153 \r
9154     while( *pc ) {\r
9155         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9156         pc++;\r
9157     }\r
9158 \r
9159     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9160 \r
9161     pc = GLT_ALL_TAGS;\r
9162 \r
9163     while( *pc ) {\r
9164         if( strchr( tags, *pc ) == 0 ) {\r
9165             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9166         }\r
9167         pc++;\r
9168     }\r
9169 \r
9170     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9171 }\r
9172 \r
9173 char GLT_ListItemToTag( HWND hDlg, int index )\r
9174 {\r
9175     char result = '\0';\r
9176     char name[128];\r
9177 \r
9178     GLT_Item * list = GLT_ItemInfo;\r
9179 \r
9180     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9181         while( list->id != 0 ) {\r
9182             if( strcmp( list->name, name ) == 0 ) {\r
9183                 result = list->id;\r
9184                 break;\r
9185             }\r
9186 \r
9187             list++;\r
9188         }\r
9189     }\r
9190 \r
9191     return result;\r
9192 }\r
9193 \r
9194 void GLT_MoveSelection( HWND hDlg, int delta )\r
9195 {\r
9196     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9197     int idx2 = idx1 + delta;\r
9198     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9199 \r
9200     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9201         char buf[128];\r
9202 \r
9203         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9204         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9205         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9206         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9207     }\r
9208 }\r
9209 \r
9210 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9211 {\r
9212     static char glt[64];\r
9213     static char * lpUserGLT;\r
9214 \r
9215     switch( message )\r
9216     {\r
9217     case WM_INITDIALOG:\r
9218         lpUserGLT = (char *) lParam;\r
9219         \r
9220         strcpy( glt, lpUserGLT );\r
9221 \r
9222         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9223 \r
9224         /* Initialize list */\r
9225         GLT_TagsToList( hDlg, glt );\r
9226 \r
9227         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9228 \r
9229         break;\r
9230 \r
9231     case WM_COMMAND:\r
9232         switch( LOWORD(wParam) ) {\r
9233         case IDOK:\r
9234             {\r
9235                 char * pc = lpUserGLT;\r
9236                 int idx = 0;\r
9237 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9238                 char id;\r
9239 \r
9240                 do {\r
9241                     id = GLT_ListItemToTag( hDlg, idx );\r
9242 \r
9243                     *pc++ = id;\r
9244                     idx++;\r
9245                 } while( id != '\0' );\r
9246             }\r
9247             EndDialog( hDlg, 0 );\r
9248             return TRUE;\r
9249         case IDCANCEL:\r
9250             EndDialog( hDlg, 1 );\r
9251             return TRUE;\r
9252 \r
9253         case IDC_GLT_Default:\r
9254             strcpy( glt, GLT_DEFAULT_TAGS );\r
9255             GLT_TagsToList( hDlg, glt );\r
9256             return TRUE;\r
9257 \r
9258         case IDC_GLT_Restore:\r
9259             strcpy( glt, lpUserGLT );\r
9260             GLT_TagsToList( hDlg, glt );\r
9261             return TRUE;\r
9262 \r
9263         case IDC_GLT_Up:\r
9264             GLT_MoveSelection( hDlg, -1 );\r
9265             return TRUE;\r
9266 \r
9267         case IDC_GLT_Down:\r
9268             GLT_MoveSelection( hDlg, +1 );\r
9269             return TRUE;\r
9270         }\r
9271 \r
9272         break;\r
9273     }\r
9274 \r
9275     return FALSE;\r
9276 }\r
9277 \r
9278 int GameListOptions()\r
9279 {\r
9280     char glt[64];\r
9281     int result;\r
9282     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9283 \r
9284     strcpy( glt, appData.gameListTags );\r
9285 \r
9286     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9287 \r
9288     if( result == 0 ) {\r
9289         /* [AS] Memory leak here! */\r
9290         appData.gameListTags = strdup( glt ); \r
9291     }\r
9292 \r
9293     return result;\r
9294 }\r
9295 \r
9296 \r
9297 VOID\r
9298 DisplayIcsInteractionTitle(char *str)\r
9299 {\r
9300   char consoleTitle[MSG_SIZ];\r
9301 \r
9302   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9303   SetWindowText(hwndConsole, consoleTitle);\r
9304 }\r
9305 \r
9306 void\r
9307 DrawPosition(int fullRedraw, Board board)\r
9308 {\r
9309   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9310 }\r
9311 \r
9312 \r
9313 VOID\r
9314 ResetFrontEnd()\r
9315 {\r
9316   fromX = fromY = -1;\r
9317   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9318     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9319     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9320     dragInfo.lastpos = dragInfo.pos;\r
9321     dragInfo.start.x = dragInfo.start.y = -1;\r
9322     dragInfo.from = dragInfo.start;\r
9323     ReleaseCapture();\r
9324     DrawPosition(TRUE, NULL);\r
9325   }\r
9326 }\r
9327 \r
9328 \r
9329 VOID\r
9330 CommentPopUp(char *title, char *str)\r
9331 {\r
9332   HWND hwnd = GetActiveWindow();\r
9333   EitherCommentPopUp(0, title, str, FALSE);\r
9334   SetActiveWindow(hwnd);\r
9335 }\r
9336 \r
9337 VOID\r
9338 CommentPopDown(void)\r
9339 {\r
9340   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9341   if (commentDialog) {\r
9342     ShowWindow(commentDialog, SW_HIDE);\r
9343   }\r
9344   commentDialogUp = FALSE;\r
9345 }\r
9346 \r
9347 VOID\r
9348 EditCommentPopUp(int index, char *title, char *str)\r
9349 {\r
9350   EitherCommentPopUp(index, title, str, TRUE);\r
9351 }\r
9352 \r
9353 \r
9354 VOID\r
9355 RingBell()\r
9356 {\r
9357   MyPlaySound(&sounds[(int)SoundMove]);\r
9358 }\r
9359 \r
9360 VOID PlayIcsWinSound()\r
9361 {\r
9362   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9363 }\r
9364 \r
9365 VOID PlayIcsLossSound()\r
9366 {\r
9367   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9368 }\r
9369 \r
9370 VOID PlayIcsDrawSound()\r
9371 {\r
9372   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9373 }\r
9374 \r
9375 VOID PlayIcsUnfinishedSound()\r
9376 {\r
9377   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9378 }\r
9379 \r
9380 VOID\r
9381 PlayAlarmSound()\r
9382 {\r
9383   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9384 }\r
9385 \r
9386 \r
9387 VOID\r
9388 EchoOn()\r
9389 {\r
9390   HWND hInput;\r
9391   consoleEcho = TRUE;\r
9392   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9393   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9394   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9395 }\r
9396 \r
9397 \r
9398 VOID\r
9399 EchoOff()\r
9400 {\r
9401   CHARFORMAT cf;\r
9402   HWND hInput;\r
9403   consoleEcho = FALSE;\r
9404   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9405   /* This works OK: set text and background both to the same color */\r
9406   cf = consoleCF;\r
9407   cf.crTextColor = COLOR_ECHOOFF;\r
9408   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9409   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9410 }\r
9411 \r
9412 /* No Raw()...? */\r
9413 \r
9414 void Colorize(ColorClass cc, int continuation)\r
9415 {\r
9416   currentColorClass = cc;\r
9417   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9418   consoleCF.crTextColor = textAttribs[cc].color;\r
9419   consoleCF.dwEffects = textAttribs[cc].effects;\r
9420   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9421 }\r
9422 \r
9423 char *\r
9424 UserName()\r
9425 {\r
9426   static char buf[MSG_SIZ];\r
9427   DWORD bufsiz = MSG_SIZ;\r
9428 \r
9429   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9430         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9431   }\r
9432   if (!GetUserName(buf, &bufsiz)) {\r
9433     /*DisplayError("Error getting user name", GetLastError());*/\r
9434     strcpy(buf, "User");\r
9435   }\r
9436   return buf;\r
9437 }\r
9438 \r
9439 char *\r
9440 HostName()\r
9441 {\r
9442   static char buf[MSG_SIZ];\r
9443   DWORD bufsiz = MSG_SIZ;\r
9444 \r
9445   if (!GetComputerName(buf, &bufsiz)) {\r
9446     /*DisplayError("Error getting host name", GetLastError());*/\r
9447     strcpy(buf, "Unknown");\r
9448   }\r
9449   return buf;\r
9450 }\r
9451 \r
9452 \r
9453 int\r
9454 ClockTimerRunning()\r
9455 {\r
9456   return clockTimerEvent != 0;\r
9457 }\r
9458 \r
9459 int\r
9460 StopClockTimer()\r
9461 {\r
9462   if (clockTimerEvent == 0) return FALSE;\r
9463   KillTimer(hwndMain, clockTimerEvent);\r
9464   clockTimerEvent = 0;\r
9465   return TRUE;\r
9466 }\r
9467 \r
9468 void\r
9469 StartClockTimer(long millisec)\r
9470 {\r
9471   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9472                              (UINT) millisec, NULL);\r
9473 }\r
9474 \r
9475 void\r
9476 DisplayWhiteClock(long timeRemaining, int highlight)\r
9477 {\r
9478   HDC hdc;\r
9479   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9480 \r
9481   if(appData.noGUI) return;\r
9482   hdc = GetDC(hwndMain);\r
9483   if (!IsIconic(hwndMain)) {\r
9484     DisplayAClock(hdc, timeRemaining, highlight, \r
9485                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9486   }\r
9487   if (highlight && iconCurrent == iconBlack) {\r
9488     iconCurrent = iconWhite;\r
9489     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9490     if (IsIconic(hwndMain)) {\r
9491       DrawIcon(hdc, 2, 2, iconCurrent);\r
9492     }\r
9493   }\r
9494   (void) ReleaseDC(hwndMain, hdc);\r
9495   if (hwndConsole)\r
9496     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9497 }\r
9498 \r
9499 void\r
9500 DisplayBlackClock(long timeRemaining, int highlight)\r
9501 {\r
9502   HDC hdc;\r
9503   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9504 \r
9505   if(appData.noGUI) return;\r
9506   hdc = GetDC(hwndMain);\r
9507   if (!IsIconic(hwndMain)) {\r
9508     DisplayAClock(hdc, timeRemaining, highlight, \r
9509                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9510   }\r
9511   if (highlight && iconCurrent == iconWhite) {\r
9512     iconCurrent = iconBlack;\r
9513     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9514     if (IsIconic(hwndMain)) {\r
9515       DrawIcon(hdc, 2, 2, iconCurrent);\r
9516     }\r
9517   }\r
9518   (void) ReleaseDC(hwndMain, hdc);\r
9519   if (hwndConsole)\r
9520     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9521 }\r
9522 \r
9523 \r
9524 int\r
9525 LoadGameTimerRunning()\r
9526 {\r
9527   return loadGameTimerEvent != 0;\r
9528 }\r
9529 \r
9530 int\r
9531 StopLoadGameTimer()\r
9532 {\r
9533   if (loadGameTimerEvent == 0) return FALSE;\r
9534   KillTimer(hwndMain, loadGameTimerEvent);\r
9535   loadGameTimerEvent = 0;\r
9536   return TRUE;\r
9537 }\r
9538 \r
9539 void\r
9540 StartLoadGameTimer(long millisec)\r
9541 {\r
9542   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9543                                 (UINT) millisec, NULL);\r
9544 }\r
9545 \r
9546 void\r
9547 AutoSaveGame()\r
9548 {\r
9549   char *defName;\r
9550   FILE *f;\r
9551   char fileTitle[MSG_SIZ];\r
9552 \r
9553   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9554   f = OpenFileDialog(hwndMain, "a", defName,\r
9555                      appData.oldSaveStyle ? "gam" : "pgn",\r
9556                      GAME_FILT, \r
9557                      "Save Game to File", NULL, fileTitle, NULL);\r
9558   if (f != NULL) {\r
9559     SaveGame(f, 0, "");\r
9560     fclose(f);\r
9561   }\r
9562 }\r
9563 \r
9564 \r
9565 void\r
9566 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9567 {\r
9568   if (delayedTimerEvent != 0) {\r
9569     if (appData.debugMode) {\r
9570       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9571     }\r
9572     KillTimer(hwndMain, delayedTimerEvent);\r
9573     delayedTimerEvent = 0;\r
9574     delayedTimerCallback();\r
9575   }\r
9576   delayedTimerCallback = cb;\r
9577   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9578                                 (UINT) millisec, NULL);\r
9579 }\r
9580 \r
9581 DelayedEventCallback\r
9582 GetDelayedEvent()\r
9583 {\r
9584   if (delayedTimerEvent) {\r
9585     return delayedTimerCallback;\r
9586   } else {\r
9587     return NULL;\r
9588   }\r
9589 }\r
9590 \r
9591 void\r
9592 CancelDelayedEvent()\r
9593 {\r
9594   if (delayedTimerEvent) {\r
9595     KillTimer(hwndMain, delayedTimerEvent);\r
9596     delayedTimerEvent = 0;\r
9597   }\r
9598 }\r
9599 \r
9600 DWORD GetWin32Priority(int nice)\r
9601 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9602 /*\r
9603 REALTIME_PRIORITY_CLASS     0x00000100\r
9604 HIGH_PRIORITY_CLASS         0x00000080\r
9605 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9606 NORMAL_PRIORITY_CLASS       0x00000020\r
9607 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9608 IDLE_PRIORITY_CLASS         0x00000040\r
9609 */\r
9610         if (nice < -15) return 0x00000080;\r
9611         if (nice < 0)   return 0x00008000;\r
9612         if (nice == 0)  return 0x00000020;\r
9613         if (nice < 15)  return 0x00004000;\r
9614         return 0x00000040;\r
9615 }\r
9616 \r
9617 /* Start a child process running the given program.\r
9618    The process's standard output can be read from "from", and its\r
9619    standard input can be written to "to".\r
9620    Exit with fatal error if anything goes wrong.\r
9621    Returns an opaque pointer that can be used to destroy the process\r
9622    later.\r
9623 */\r
9624 int\r
9625 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9626 {\r
9627 #define BUFSIZE 4096\r
9628 \r
9629   HANDLE hChildStdinRd, hChildStdinWr,\r
9630     hChildStdoutRd, hChildStdoutWr;\r
9631   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9632   SECURITY_ATTRIBUTES saAttr;\r
9633   BOOL fSuccess;\r
9634   PROCESS_INFORMATION piProcInfo;\r
9635   STARTUPINFO siStartInfo;\r
9636   ChildProc *cp;\r
9637   char buf[MSG_SIZ];\r
9638   DWORD err;\r
9639 \r
9640   if (appData.debugMode) {\r
9641     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9642   }\r
9643 \r
9644   *pr = NoProc;\r
9645 \r
9646   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9647   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9648   saAttr.bInheritHandle = TRUE;\r
9649   saAttr.lpSecurityDescriptor = NULL;\r
9650 \r
9651   /*\r
9652    * The steps for redirecting child's STDOUT:\r
9653    *     1. Create anonymous pipe to be STDOUT for child.\r
9654    *     2. Create a noninheritable duplicate of read handle,\r
9655    *         and close the inheritable read handle.\r
9656    */\r
9657 \r
9658   /* Create a pipe for the child's STDOUT. */\r
9659   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9660     return GetLastError();\r
9661   }\r
9662 \r
9663   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9664   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9665                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9666                              FALSE,     /* not inherited */\r
9667                              DUPLICATE_SAME_ACCESS);\r
9668   if (! fSuccess) {\r
9669     return GetLastError();\r
9670   }\r
9671   CloseHandle(hChildStdoutRd);\r
9672 \r
9673   /*\r
9674    * The steps for redirecting child's STDIN:\r
9675    *     1. Create anonymous pipe to be STDIN for child.\r
9676    *     2. Create a noninheritable duplicate of write handle,\r
9677    *         and close the inheritable write handle.\r
9678    */\r
9679 \r
9680   /* Create a pipe for the child's STDIN. */\r
9681   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9682     return GetLastError();\r
9683   }\r
9684 \r
9685   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9686   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9687                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9688                              FALSE,     /* not inherited */\r
9689                              DUPLICATE_SAME_ACCESS);\r
9690   if (! fSuccess) {\r
9691     return GetLastError();\r
9692   }\r
9693   CloseHandle(hChildStdinWr);\r
9694 \r
9695   /* Arrange to (1) look in dir for the child .exe file, and\r
9696    * (2) have dir be the child's working directory.  Interpret\r
9697    * dir relative to the directory WinBoard loaded from. */\r
9698   GetCurrentDirectory(MSG_SIZ, buf);\r
9699   SetCurrentDirectory(installDir);\r
9700   SetCurrentDirectory(dir);\r
9701 \r
9702   /* Now create the child process. */\r
9703 \r
9704   siStartInfo.cb = sizeof(STARTUPINFO);\r
9705   siStartInfo.lpReserved = NULL;\r
9706   siStartInfo.lpDesktop = NULL;\r
9707   siStartInfo.lpTitle = NULL;\r
9708   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9709   siStartInfo.cbReserved2 = 0;\r
9710   siStartInfo.lpReserved2 = NULL;\r
9711   siStartInfo.hStdInput = hChildStdinRd;\r
9712   siStartInfo.hStdOutput = hChildStdoutWr;\r
9713   siStartInfo.hStdError = hChildStdoutWr;\r
9714 \r
9715   fSuccess = CreateProcess(NULL,\r
9716                            cmdLine,        /* command line */\r
9717                            NULL,           /* process security attributes */\r
9718                            NULL,           /* primary thread security attrs */\r
9719                            TRUE,           /* handles are inherited */\r
9720                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9721                            NULL,           /* use parent's environment */\r
9722                            NULL,\r
9723                            &siStartInfo, /* STARTUPINFO pointer */\r
9724                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9725 \r
9726   err = GetLastError();\r
9727   SetCurrentDirectory(buf); /* return to prev directory */\r
9728   if (! fSuccess) {\r
9729     return err;\r
9730   }\r
9731 \r
9732   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9733     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9734     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9735   }\r
9736 \r
9737   /* Close the handles we don't need in the parent */\r
9738   CloseHandle(piProcInfo.hThread);\r
9739   CloseHandle(hChildStdinRd);\r
9740   CloseHandle(hChildStdoutWr);\r
9741 \r
9742   /* Prepare return value */\r
9743   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9744   cp->kind = CPReal;\r
9745   cp->hProcess = piProcInfo.hProcess;\r
9746   cp->pid = piProcInfo.dwProcessId;\r
9747   cp->hFrom = hChildStdoutRdDup;\r
9748   cp->hTo = hChildStdinWrDup;\r
9749 \r
9750   *pr = (void *) cp;\r
9751 \r
9752   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9753      2000 where engines sometimes don't see the initial command(s)\r
9754      from WinBoard and hang.  I don't understand how that can happen,\r
9755      but the Sleep is harmless, so I've put it in.  Others have also\r
9756      reported what may be the same problem, so hopefully this will fix\r
9757      it for them too.  */\r
9758   Sleep(500);\r
9759 \r
9760   return NO_ERROR;\r
9761 }\r
9762 \r
9763 \r
9764 void\r
9765 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9766 {\r
9767   ChildProc *cp; int result;\r
9768 \r
9769   cp = (ChildProc *) pr;\r
9770   if (cp == NULL) return;\r
9771 \r
9772   switch (cp->kind) {\r
9773   case CPReal:\r
9774     /* TerminateProcess is considered harmful, so... */\r
9775     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9776     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9777     /* The following doesn't work because the chess program\r
9778        doesn't "have the same console" as WinBoard.  Maybe\r
9779        we could arrange for this even though neither WinBoard\r
9780        nor the chess program uses a console for stdio? */\r
9781     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9782 \r
9783     /* [AS] Special termination modes for misbehaving programs... */\r
9784     if( signal == 9 ) { \r
9785         result = TerminateProcess( cp->hProcess, 0 );\r
9786 \r
9787         if ( appData.debugMode) {\r
9788             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9789         }\r
9790     }\r
9791     else if( signal == 10 ) {\r
9792         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9793 \r
9794         if( dw != WAIT_OBJECT_0 ) {\r
9795             result = TerminateProcess( cp->hProcess, 0 );\r
9796 \r
9797             if ( appData.debugMode) {\r
9798                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9799             }\r
9800 \r
9801         }\r
9802     }\r
9803 \r
9804     CloseHandle(cp->hProcess);\r
9805     break;\r
9806 \r
9807   case CPComm:\r
9808     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9809     break;\r
9810 \r
9811   case CPSock:\r
9812     closesocket(cp->sock);\r
9813     WSACleanup();\r
9814     break;\r
9815 \r
9816   case CPRcmd:\r
9817     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9818     closesocket(cp->sock);\r
9819     closesocket(cp->sock2);\r
9820     WSACleanup();\r
9821     break;\r
9822   }\r
9823   free(cp);\r
9824 }\r
9825 \r
9826 void\r
9827 InterruptChildProcess(ProcRef pr)\r
9828 {\r
9829   ChildProc *cp;\r
9830 \r
9831   cp = (ChildProc *) pr;\r
9832   if (cp == NULL) return;\r
9833   switch (cp->kind) {\r
9834   case CPReal:\r
9835     /* The following doesn't work because the chess program\r
9836        doesn't "have the same console" as WinBoard.  Maybe\r
9837        we could arrange for this even though neither WinBoard\r
9838        nor the chess program uses a console for stdio */\r
9839     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9840     break;\r
9841 \r
9842   case CPComm:\r
9843   case CPSock:\r
9844     /* Can't interrupt */\r
9845     break;\r
9846 \r
9847   case CPRcmd:\r
9848     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9849     break;\r
9850   }\r
9851 }\r
9852 \r
9853 \r
9854 int\r
9855 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9856 {\r
9857   char cmdLine[MSG_SIZ];\r
9858 \r
9859   if (port[0] == NULLCHAR) {\r
9860     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9861   } else {\r
9862     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9863   }\r
9864   return StartChildProcess(cmdLine, "", pr);\r
9865 }\r
9866 \r
9867 \r
9868 /* Code to open TCP sockets */\r
9869 \r
9870 int\r
9871 OpenTCP(char *host, char *port, ProcRef *pr)\r
9872 {\r
9873   ChildProc *cp;\r
9874   int err;\r
9875   SOCKET s;\r
9876   struct sockaddr_in sa, mysa;\r
9877   struct hostent FAR *hp;\r
9878   unsigned short uport;\r
9879   WORD wVersionRequested;\r
9880   WSADATA wsaData;\r
9881 \r
9882   /* Initialize socket DLL */\r
9883   wVersionRequested = MAKEWORD(1, 1);\r
9884   err = WSAStartup(wVersionRequested, &wsaData);\r
9885   if (err != 0) return err;\r
9886 \r
9887   /* Make socket */\r
9888   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9889     err = WSAGetLastError();\r
9890     WSACleanup();\r
9891     return err;\r
9892   }\r
9893 \r
9894   /* Bind local address using (mostly) don't-care values.\r
9895    */\r
9896   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9897   mysa.sin_family = AF_INET;\r
9898   mysa.sin_addr.s_addr = INADDR_ANY;\r
9899   uport = (unsigned short) 0;\r
9900   mysa.sin_port = htons(uport);\r
9901   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9902       == SOCKET_ERROR) {\r
9903     err = WSAGetLastError();\r
9904     WSACleanup();\r
9905     return err;\r
9906   }\r
9907 \r
9908   /* Resolve remote host name */\r
9909   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9910   if (!(hp = gethostbyname(host))) {\r
9911     unsigned int b0, b1, b2, b3;\r
9912 \r
9913     err = WSAGetLastError();\r
9914 \r
9915     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9916       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9917       hp->h_addrtype = AF_INET;\r
9918       hp->h_length = 4;\r
9919       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9920       hp->h_addr_list[0] = (char *) malloc(4);\r
9921       hp->h_addr_list[0][0] = (char) b0;\r
9922       hp->h_addr_list[0][1] = (char) b1;\r
9923       hp->h_addr_list[0][2] = (char) b2;\r
9924       hp->h_addr_list[0][3] = (char) b3;\r
9925     } else {\r
9926       WSACleanup();\r
9927       return err;\r
9928     }\r
9929   }\r
9930   sa.sin_family = hp->h_addrtype;\r
9931   uport = (unsigned short) atoi(port);\r
9932   sa.sin_port = htons(uport);\r
9933   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9934 \r
9935   /* Make connection */\r
9936   if (connect(s, (struct sockaddr *) &sa,\r
9937               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9938     err = WSAGetLastError();\r
9939     WSACleanup();\r
9940     return err;\r
9941   }\r
9942 \r
9943   /* Prepare return value */\r
9944   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9945   cp->kind = CPSock;\r
9946   cp->sock = s;\r
9947   *pr = (ProcRef *) cp;\r
9948 \r
9949   return NO_ERROR;\r
9950 }\r
9951 \r
9952 int\r
9953 OpenCommPort(char *name, ProcRef *pr)\r
9954 {\r
9955   HANDLE h;\r
9956   COMMTIMEOUTS ct;\r
9957   ChildProc *cp;\r
9958   char fullname[MSG_SIZ];\r
9959 \r
9960   if (*name != '\\')\r
9961     sprintf(fullname, "\\\\.\\%s", name);\r
9962   else\r
9963     strcpy(fullname, name);\r
9964 \r
9965   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9966                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9967   if (h == (HANDLE) -1) {\r
9968     return GetLastError();\r
9969   }\r
9970   hCommPort = h;\r
9971 \r
9972   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9973 \r
9974   /* Accumulate characters until a 100ms pause, then parse */\r
9975   ct.ReadIntervalTimeout = 100;\r
9976   ct.ReadTotalTimeoutMultiplier = 0;\r
9977   ct.ReadTotalTimeoutConstant = 0;\r
9978   ct.WriteTotalTimeoutMultiplier = 0;\r
9979   ct.WriteTotalTimeoutConstant = 0;\r
9980   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9981 \r
9982   /* Prepare return value */\r
9983   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9984   cp->kind = CPComm;\r
9985   cp->hFrom = h;\r
9986   cp->hTo = h;\r
9987   *pr = (ProcRef *) cp;\r
9988 \r
9989   return NO_ERROR;\r
9990 }\r
9991 \r
9992 int\r
9993 OpenLoopback(ProcRef *pr)\r
9994 {\r
9995   DisplayFatalError("Not implemented", 0, 1);\r
9996   return NO_ERROR;\r
9997 }\r
9998 \r
9999 \r
10000 int\r
10001 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10002 {\r
10003   ChildProc *cp;\r
10004   int err;\r
10005   SOCKET s, s2, s3;\r
10006   struct sockaddr_in sa, mysa;\r
10007   struct hostent FAR *hp;\r
10008   unsigned short uport;\r
10009   WORD wVersionRequested;\r
10010   WSADATA wsaData;\r
10011   int fromPort;\r
10012   char stderrPortStr[MSG_SIZ];\r
10013 \r
10014   /* Initialize socket DLL */\r
10015   wVersionRequested = MAKEWORD(1, 1);\r
10016   err = WSAStartup(wVersionRequested, &wsaData);\r
10017   if (err != 0) return err;\r
10018 \r
10019   /* Resolve remote host name */\r
10020   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10021   if (!(hp = gethostbyname(host))) {\r
10022     unsigned int b0, b1, b2, b3;\r
10023 \r
10024     err = WSAGetLastError();\r
10025 \r
10026     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10027       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10028       hp->h_addrtype = AF_INET;\r
10029       hp->h_length = 4;\r
10030       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10031       hp->h_addr_list[0] = (char *) malloc(4);\r
10032       hp->h_addr_list[0][0] = (char) b0;\r
10033       hp->h_addr_list[0][1] = (char) b1;\r
10034       hp->h_addr_list[0][2] = (char) b2;\r
10035       hp->h_addr_list[0][3] = (char) b3;\r
10036     } else {\r
10037       WSACleanup();\r
10038       return err;\r
10039     }\r
10040   }\r
10041   sa.sin_family = hp->h_addrtype;\r
10042   uport = (unsigned short) 514;\r
10043   sa.sin_port = htons(uport);\r
10044   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10045 \r
10046   /* Bind local socket to unused "privileged" port address\r
10047    */\r
10048   s = INVALID_SOCKET;\r
10049   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10050   mysa.sin_family = AF_INET;\r
10051   mysa.sin_addr.s_addr = INADDR_ANY;\r
10052   for (fromPort = 1023;; fromPort--) {\r
10053     if (fromPort < 0) {\r
10054       WSACleanup();\r
10055       return WSAEADDRINUSE;\r
10056     }\r
10057     if (s == INVALID_SOCKET) {\r
10058       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10059         err = WSAGetLastError();\r
10060         WSACleanup();\r
10061         return err;\r
10062       }\r
10063     }\r
10064     uport = (unsigned short) fromPort;\r
10065     mysa.sin_port = htons(uport);\r
10066     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10067         == SOCKET_ERROR) {\r
10068       err = WSAGetLastError();\r
10069       if (err == WSAEADDRINUSE) continue;\r
10070       WSACleanup();\r
10071       return err;\r
10072     }\r
10073     if (connect(s, (struct sockaddr *) &sa,\r
10074       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10075       err = WSAGetLastError();\r
10076       if (err == WSAEADDRINUSE) {\r
10077         closesocket(s);\r
10078         s = -1;\r
10079         continue;\r
10080       }\r
10081       WSACleanup();\r
10082       return err;\r
10083     }\r
10084     break;\r
10085   }\r
10086 \r
10087   /* Bind stderr local socket to unused "privileged" port address\r
10088    */\r
10089   s2 = INVALID_SOCKET;\r
10090   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10091   mysa.sin_family = AF_INET;\r
10092   mysa.sin_addr.s_addr = INADDR_ANY;\r
10093   for (fromPort = 1023;; fromPort--) {\r
10094     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10095     if (fromPort < 0) {\r
10096       (void) closesocket(s);\r
10097       WSACleanup();\r
10098       return WSAEADDRINUSE;\r
10099     }\r
10100     if (s2 == INVALID_SOCKET) {\r
10101       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10102         err = WSAGetLastError();\r
10103         closesocket(s);\r
10104         WSACleanup();\r
10105         return err;\r
10106       }\r
10107     }\r
10108     uport = (unsigned short) fromPort;\r
10109     mysa.sin_port = htons(uport);\r
10110     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10111         == SOCKET_ERROR) {\r
10112       err = WSAGetLastError();\r
10113       if (err == WSAEADDRINUSE) continue;\r
10114       (void) closesocket(s);\r
10115       WSACleanup();\r
10116       return err;\r
10117     }\r
10118     if (listen(s2, 1) == SOCKET_ERROR) {\r
10119       err = WSAGetLastError();\r
10120       if (err == WSAEADDRINUSE) {\r
10121         closesocket(s2);\r
10122         s2 = INVALID_SOCKET;\r
10123         continue;\r
10124       }\r
10125       (void) closesocket(s);\r
10126       (void) closesocket(s2);\r
10127       WSACleanup();\r
10128       return err;\r
10129     }\r
10130     break;\r
10131   }\r
10132   prevStderrPort = fromPort; // remember port used\r
10133   sprintf(stderrPortStr, "%d", fromPort);\r
10134 \r
10135   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10136     err = WSAGetLastError();\r
10137     (void) closesocket(s);\r
10138     (void) closesocket(s2);\r
10139     WSACleanup();\r
10140     return err;\r
10141   }\r
10142 \r
10143   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10144     err = WSAGetLastError();\r
10145     (void) closesocket(s);\r
10146     (void) closesocket(s2);\r
10147     WSACleanup();\r
10148     return err;\r
10149   }\r
10150   if (*user == NULLCHAR) user = UserName();\r
10151   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10152     err = WSAGetLastError();\r
10153     (void) closesocket(s);\r
10154     (void) closesocket(s2);\r
10155     WSACleanup();\r
10156     return err;\r
10157   }\r
10158   if (send(s, cmd, strlen(cmd) + 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 \r
10166   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10167     err = WSAGetLastError();\r
10168     (void) closesocket(s);\r
10169     (void) closesocket(s2);\r
10170     WSACleanup();\r
10171     return err;\r
10172   }\r
10173   (void) closesocket(s2);  /* Stop listening */\r
10174 \r
10175   /* Prepare return value */\r
10176   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10177   cp->kind = CPRcmd;\r
10178   cp->sock = s;\r
10179   cp->sock2 = s3;\r
10180   *pr = (ProcRef *) cp;\r
10181 \r
10182   return NO_ERROR;\r
10183 }\r
10184 \r
10185 \r
10186 InputSourceRef\r
10187 AddInputSource(ProcRef pr, int lineByLine,\r
10188                InputCallback func, VOIDSTAR closure)\r
10189 {\r
10190   InputSource *is, *is2 = NULL;\r
10191   ChildProc *cp = (ChildProc *) pr;\r
10192 \r
10193   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10194   is->lineByLine = lineByLine;\r
10195   is->func = func;\r
10196   is->closure = closure;\r
10197   is->second = NULL;\r
10198   is->next = is->buf;\r
10199   if (pr == NoProc) {\r
10200     is->kind = CPReal;\r
10201     consoleInputSource = is;\r
10202   } else {\r
10203     is->kind = cp->kind;\r
10204     /* \r
10205         [AS] Try to avoid a race condition if the thread is given control too early:\r
10206         we create all threads suspended so that the is->hThread variable can be\r
10207         safely assigned, then let the threads start with ResumeThread.\r
10208     */\r
10209     switch (cp->kind) {\r
10210     case CPReal:\r
10211       is->hFile = cp->hFrom;\r
10212       cp->hFrom = NULL; /* now owned by InputThread */\r
10213       is->hThread =\r
10214         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10215                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10216       break;\r
10217 \r
10218     case CPComm:\r
10219       is->hFile = cp->hFrom;\r
10220       cp->hFrom = NULL; /* now owned by InputThread */\r
10221       is->hThread =\r
10222         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10223                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10224       break;\r
10225 \r
10226     case CPSock:\r
10227       is->sock = cp->sock;\r
10228       is->hThread =\r
10229         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10230                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10231       break;\r
10232 \r
10233     case CPRcmd:\r
10234       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10235       *is2 = *is;\r
10236       is->sock = cp->sock;\r
10237       is->second = is2;\r
10238       is2->sock = cp->sock2;\r
10239       is2->second = is2;\r
10240       is->hThread =\r
10241         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10242                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10243       is2->hThread =\r
10244         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10245                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10246       break;\r
10247     }\r
10248 \r
10249     if( is->hThread != NULL ) {\r
10250         ResumeThread( is->hThread );\r
10251     }\r
10252 \r
10253     if( is2 != NULL && is2->hThread != NULL ) {\r
10254         ResumeThread( is2->hThread );\r
10255     }\r
10256   }\r
10257 \r
10258   return (InputSourceRef) is;\r
10259 }\r
10260 \r
10261 void\r
10262 RemoveInputSource(InputSourceRef isr)\r
10263 {\r
10264   InputSource *is;\r
10265 \r
10266   is = (InputSource *) isr;\r
10267   is->hThread = NULL;  /* tell thread to stop */\r
10268   CloseHandle(is->hThread);\r
10269   if (is->second != NULL) {\r
10270     is->second->hThread = NULL;\r
10271     CloseHandle(is->second->hThread);\r
10272   }\r
10273 }\r
10274 \r
10275 \r
10276 int\r
10277 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10278 {\r
10279   DWORD dOutCount;\r
10280   int outCount = SOCKET_ERROR;\r
10281   ChildProc *cp = (ChildProc *) pr;\r
10282   static OVERLAPPED ovl;\r
10283 \r
10284   if (pr == NoProc) {\r
10285     ConsoleOutput(message, count, FALSE);\r
10286     return count;\r
10287   } \r
10288 \r
10289   if (ovl.hEvent == NULL) {\r
10290     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10291   }\r
10292   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10293 \r
10294   switch (cp->kind) {\r
10295   case CPSock:\r
10296   case CPRcmd:\r
10297     outCount = send(cp->sock, message, count, 0);\r
10298     if (outCount == SOCKET_ERROR) {\r
10299       *outError = WSAGetLastError();\r
10300     } else {\r
10301       *outError = NO_ERROR;\r
10302     }\r
10303     break;\r
10304 \r
10305   case CPReal:\r
10306     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10307                   &dOutCount, NULL)) {\r
10308       *outError = NO_ERROR;\r
10309       outCount = (int) dOutCount;\r
10310     } else {\r
10311       *outError = GetLastError();\r
10312     }\r
10313     break;\r
10314 \r
10315   case CPComm:\r
10316     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10317                             &dOutCount, &ovl);\r
10318     if (*outError == NO_ERROR) {\r
10319       outCount = (int) dOutCount;\r
10320     }\r
10321     break;\r
10322   }\r
10323   return outCount;\r
10324 }\r
10325 \r
10326 int\r
10327 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10328                        long msdelay)\r
10329 {\r
10330   /* Ignore delay, not implemented for WinBoard */\r
10331   return OutputToProcess(pr, message, count, outError);\r
10332 }\r
10333 \r
10334 \r
10335 void\r
10336 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10337                         char *buf, int count, int error)\r
10338 {\r
10339   DisplayFatalError("Not implemented", 0, 1);\r
10340 }\r
10341 \r
10342 /* see wgamelist.c for Game List functions */\r
10343 /* see wedittags.c for Edit Tags functions */\r
10344 \r
10345 \r
10346 VOID\r
10347 ICSInitScript()\r
10348 {\r
10349   FILE *f;\r
10350   char buf[MSG_SIZ];\r
10351   char *dummy;\r
10352 \r
10353   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10354     f = fopen(buf, "r");\r
10355     if (f != NULL) {\r
10356       ProcessICSInitScript(f);\r
10357       fclose(f);\r
10358     }\r
10359   }\r
10360 }\r
10361 \r
10362 \r
10363 VOID\r
10364 StartAnalysisClock()\r
10365 {\r
10366   if (analysisTimerEvent) return;\r
10367   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10368                                         (UINT) 2000, NULL);\r
10369 }\r
10370 \r
10371 LRESULT CALLBACK\r
10372 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10373 {\r
10374   static HANDLE hwndText;\r
10375   RECT rect;\r
10376   static int sizeX, sizeY;\r
10377   int newSizeX, newSizeY, flags;\r
10378   MINMAXINFO *mmi;\r
10379 \r
10380   switch (message) {\r
10381   case WM_INITDIALOG: /* message: initialize dialog box */\r
10382     /* Initialize the dialog items */\r
10383     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10384     SetWindowText(hDlg, analysisTitle);\r
10385     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10386     /* Size and position the dialog */\r
10387     if (!analysisDialog) {\r
10388       analysisDialog = hDlg;\r
10389       flags = SWP_NOZORDER;\r
10390       GetClientRect(hDlg, &rect);\r
10391       sizeX = rect.right;\r
10392       sizeY = rect.bottom;\r
10393       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10394           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10395         WINDOWPLACEMENT wp;\r
10396         EnsureOnScreen(&analysisX, &analysisY);\r
10397         wp.length = sizeof(WINDOWPLACEMENT);\r
10398         wp.flags = 0;\r
10399         wp.showCmd = SW_SHOW;\r
10400         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10401         wp.rcNormalPosition.left = analysisX;\r
10402         wp.rcNormalPosition.right = analysisX + analysisW;\r
10403         wp.rcNormalPosition.top = analysisY;\r
10404         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10405         SetWindowPlacement(hDlg, &wp);\r
10406 \r
10407         GetClientRect(hDlg, &rect);\r
10408         newSizeX = rect.right;\r
10409         newSizeY = rect.bottom;\r
10410         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10411                               newSizeX, newSizeY);\r
10412         sizeX = newSizeX;\r
10413         sizeY = newSizeY;\r
10414       }\r
10415     }\r
10416     return FALSE;\r
10417 \r
10418   case WM_COMMAND: /* message: received a command */\r
10419     switch (LOWORD(wParam)) {\r
10420     case IDCANCEL:\r
10421       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10422           ExitAnalyzeMode();\r
10423           ModeHighlight();\r
10424           return TRUE;\r
10425       }\r
10426       EditGameEvent();\r
10427       return TRUE;\r
10428     default:\r
10429       break;\r
10430     }\r
10431     break;\r
10432 \r
10433   case WM_SIZE:\r
10434     newSizeX = LOWORD(lParam);\r
10435     newSizeY = HIWORD(lParam);\r
10436     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10437     sizeX = newSizeX;\r
10438     sizeY = newSizeY;\r
10439     break;\r
10440 \r
10441   case WM_GETMINMAXINFO:\r
10442     /* Prevent resizing window too small */\r
10443     mmi = (MINMAXINFO *) lParam;\r
10444     mmi->ptMinTrackSize.x = 100;\r
10445     mmi->ptMinTrackSize.y = 100;\r
10446     break;\r
10447   }\r
10448   return FALSE;\r
10449 }\r
10450 \r
10451 VOID\r
10452 AnalysisPopUp(char* title, char* str)\r
10453 {\r
10454   FARPROC lpProc;\r
10455   char *p, *q;\r
10456 \r
10457   /* [AS] */\r
10458   EngineOutputPopUp();\r
10459   return;\r
10460 \r
10461   if (str == NULL) str = "";\r
10462   p = (char *) malloc(2 * strlen(str) + 2);\r
10463   q = p;\r
10464   while (*str) {\r
10465     if (*str == '\n') *q++ = '\r';\r
10466     *q++ = *str++;\r
10467   }\r
10468   *q = NULLCHAR;\r
10469   if (analysisText != NULL) free(analysisText);\r
10470   analysisText = p;\r
10471 \r
10472   if (analysisDialog) {\r
10473     SetWindowText(analysisDialog, title);\r
10474     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10475     ShowWindow(analysisDialog, SW_SHOW);\r
10476   } else {\r
10477     analysisTitle = title;\r
10478     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10479     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10480                  hwndMain, (DLGPROC)lpProc);\r
10481     FreeProcInstance(lpProc);\r
10482   }\r
10483   analysisDialogUp = TRUE;  \r
10484 }\r
10485 \r
10486 VOID\r
10487 AnalysisPopDown()\r
10488 {\r
10489   if (analysisDialog) {\r
10490     ShowWindow(analysisDialog, SW_HIDE);\r
10491   }\r
10492   analysisDialogUp = FALSE;  \r
10493 }\r
10494 \r
10495 \r
10496 VOID\r
10497 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10498 {\r
10499   highlightInfo.sq[0].x = fromX;\r
10500   highlightInfo.sq[0].y = fromY;\r
10501   highlightInfo.sq[1].x = toX;\r
10502   highlightInfo.sq[1].y = toY;\r
10503 }\r
10504 \r
10505 VOID\r
10506 ClearHighlights()\r
10507 {\r
10508   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10509     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10510 }\r
10511 \r
10512 VOID\r
10513 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10514 {\r
10515   premoveHighlightInfo.sq[0].x = fromX;\r
10516   premoveHighlightInfo.sq[0].y = fromY;\r
10517   premoveHighlightInfo.sq[1].x = toX;\r
10518   premoveHighlightInfo.sq[1].y = toY;\r
10519 }\r
10520 \r
10521 VOID\r
10522 ClearPremoveHighlights()\r
10523 {\r
10524   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10525     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10526 }\r
10527 \r
10528 VOID\r
10529 ShutDownFrontEnd()\r
10530 {\r
10531   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10532   DeleteClipboardTempFiles();\r
10533 }\r
10534 \r
10535 void\r
10536 BoardToTop()\r
10537 {\r
10538     if (IsIconic(hwndMain))\r
10539       ShowWindow(hwndMain, SW_RESTORE);\r
10540 \r
10541     SetActiveWindow(hwndMain);\r
10542 }\r
10543 \r
10544 /*\r
10545  * Prototypes for animation support routines\r
10546  */\r
10547 static void ScreenSquare(int column, int row, POINT * pt);\r
10548 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10549      POINT frames[], int * nFrames);\r
10550 \r
10551 \r
10552 #define kFactor 4\r
10553 \r
10554 void\r
10555 AnimateMove(board, fromX, fromY, toX, toY)\r
10556      Board board;\r
10557      int fromX;\r
10558      int fromY;\r
10559      int toX;\r
10560      int toY;\r
10561 {\r
10562   ChessSquare piece;\r
10563   POINT start, finish, mid;\r
10564   POINT frames[kFactor * 2 + 1];\r
10565   int nFrames, n;\r
10566 \r
10567   if (!appData.animate) return;\r
10568   if (doingSizing) return;\r
10569   if (fromY < 0 || fromX < 0) return;\r
10570   piece = board[fromY][fromX];\r
10571   if (piece >= EmptySquare) return;\r
10572 \r
10573   ScreenSquare(fromX, fromY, &start);\r
10574   ScreenSquare(toX, toY, &finish);\r
10575 \r
10576   /* All pieces except knights move in straight line */\r
10577   if (piece != WhiteKnight && piece != BlackKnight) {\r
10578     mid.x = start.x + (finish.x - start.x) / 2;\r
10579     mid.y = start.y + (finish.y - start.y) / 2;\r
10580   } else {\r
10581     /* Knight: make diagonal movement then straight */\r
10582     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10583        mid.x = start.x + (finish.x - start.x) / 2;\r
10584        mid.y = finish.y;\r
10585      } else {\r
10586        mid.x = finish.x;\r
10587        mid.y = start.y + (finish.y - start.y) / 2;\r
10588      }\r
10589   }\r
10590   \r
10591   /* Don't use as many frames for very short moves */\r
10592   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10593     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10594   else\r
10595     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10596 \r
10597   animInfo.from.x = fromX;\r
10598   animInfo.from.y = fromY;\r
10599   animInfo.to.x = toX;\r
10600   animInfo.to.y = toY;\r
10601   animInfo.lastpos = start;\r
10602   animInfo.piece = piece;\r
10603   for (n = 0; n < nFrames; n++) {\r
10604     animInfo.pos = frames[n];\r
10605     DrawPosition(FALSE, NULL);\r
10606     animInfo.lastpos = animInfo.pos;\r
10607     Sleep(appData.animSpeed);\r
10608   }\r
10609   animInfo.pos = finish;\r
10610   DrawPosition(FALSE, NULL);\r
10611   animInfo.piece = EmptySquare;\r
10612 }\r
10613 \r
10614 /*      Convert board position to corner of screen rect and color       */\r
10615 \r
10616 static void\r
10617 ScreenSquare(column, row, pt)\r
10618      int column; int row; POINT * pt;\r
10619 {\r
10620   if (flipView) {\r
10621     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10622     pt->y = lineGap + row * (squareSize + lineGap);\r
10623   } else {\r
10624     pt->x = lineGap + column * (squareSize + lineGap);\r
10625     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10626   }\r
10627 }\r
10628 \r
10629 /*      Generate a series of frame coords from start->mid->finish.\r
10630         The movement rate doubles until the half way point is\r
10631         reached, then halves back down to the final destination,\r
10632         which gives a nice slow in/out effect. The algorithmn\r
10633         may seem to generate too many intermediates for short\r
10634         moves, but remember that the purpose is to attract the\r
10635         viewers attention to the piece about to be moved and\r
10636         then to where it ends up. Too few frames would be less\r
10637         noticeable.                                             */\r
10638 \r
10639 static void\r
10640 Tween(start, mid, finish, factor, frames, nFrames)\r
10641      POINT * start; POINT * mid;\r
10642      POINT * finish; int factor;\r
10643      POINT frames[]; int * nFrames;\r
10644 {\r
10645   int n, fraction = 1, count = 0;\r
10646 \r
10647   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10648   for (n = 0; n < factor; n++)\r
10649     fraction *= 2;\r
10650   for (n = 0; n < factor; n++) {\r
10651     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10652     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10653     count ++;\r
10654     fraction = fraction / 2;\r
10655   }\r
10656   \r
10657   /* Midpoint */\r
10658   frames[count] = *mid;\r
10659   count ++;\r
10660   \r
10661   /* Slow out, stepping 1/2, then 1/4, ... */\r
10662   fraction = 2;\r
10663   for (n = 0; n < factor; n++) {\r
10664     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10665     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10666     count ++;\r
10667     fraction = fraction * 2;\r
10668   }\r
10669   *nFrames = count;\r
10670 }\r
10671 \r
10672 void\r
10673 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10674 {\r
10675 #if 0\r
10676     char buf[256];\r
10677 \r
10678     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10679         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10680 \r
10681     OutputDebugString( buf );\r
10682 #endif\r
10683 \r
10684     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10685 \r
10686     EvalGraphSet( first, last, current, pvInfoList );\r
10687 }\r
10688 \r
10689 void SetProgramStats( FrontEndProgramStats * stats )\r
10690 {\r
10691 #if 0\r
10692     char buf[1024];\r
10693 \r
10694     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10695         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10696 \r
10697     OutputDebugString( buf );\r
10698 #endif\r
10699 \r
10700     EngineOutputUpdate( stats );\r
10701 }\r