changes from H.G. Muller; version 4.3.7
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect;\r
144 static char messageText[MESSAGE_TEXT_MAX];\r
145 static int clockTimerEvent = 0;\r
146 static int loadGameTimerEvent = 0;\r
147 static int analysisTimerEvent = 0;\r
148 static DelayedEventCallback delayedTimerCallback;\r
149 static int delayedTimerEvent = 0;\r
150 static int buttonCount = 2;\r
151 char *icsTextMenuString;\r
152 char *icsNames;\r
153 char *firstChessProgramNames;\r
154 char *secondChessProgramNames;\r
155 \r
156 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
157 \r
158 #define PALETTESIZE 256\r
159 \r
160 HINSTANCE hInst;          /* current instance */\r
161 HWND hwndMain = NULL;        /* root window*/\r
162 HWND hwndConsole = NULL;\r
163 BOOLEAN alwaysOnTop = FALSE;\r
164 RECT boardRect;\r
165 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
166   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
167 HPALETTE hPal;\r
168 ColorClass currentColorClass;\r
169 \r
170 HWND hCommPort = NULL;    /* currently open comm port */\r
171 static HWND hwndPause;    /* pause button */\r
172 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
173 static HBRUSH lightSquareBrush, darkSquareBrush,\r
174   blackSquareBrush, /* [HGM] for band between board and holdings */\r
175   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
176 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
177 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
178 static HPEN gridPen = NULL;\r
179 static HPEN highlightPen = NULL;\r
180 static HPEN premovePen = NULL;\r
181 static NPLOGPALETTE pLogPal;\r
182 static BOOL paletteChanged = FALSE;\r
183 static HICON iconWhite, iconBlack, iconCurrent;\r
184 static int doingSizing = FALSE;\r
185 static int lastSizing = 0;\r
186 static int prevStderrPort;\r
187 \r
188 /* [AS] Support for background textures */\r
189 #define BACK_TEXTURE_MODE_DISABLED      0\r
190 #define BACK_TEXTURE_MODE_PLAIN         1\r
191 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
192 \r
193 static HBITMAP liteBackTexture = NULL;\r
194 static HBITMAP darkBackTexture = NULL;\r
195 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
196 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int backTextureSquareSize = 0;\r
198 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
199 \r
200 #if __GNUC__ && !defined(_winmajor)\r
201 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
202 #else\r
203 #define oldDialog (_winmajor < 4)\r
204 #endif\r
205 \r
206 char *defaultTextAttribs[] = \r
207 {\r
208   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
209   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
210   COLOR_NONE\r
211 };\r
212 \r
213 typedef struct {\r
214   char *name;\r
215   int squareSize;\r
216   int lineGap;\r
217   int smallLayout;\r
218   int tinyLayout;\r
219   int cliWidth, cliHeight;\r
220 } SizeInfo;\r
221 \r
222 SizeInfo sizeInfo[] = \r
223 {\r
224   { "tiny",     21, 0, 1, 1, 0, 0 },\r
225   { "teeny",    25, 1, 1, 1, 0, 0 },\r
226   { "dinky",    29, 1, 1, 1, 0, 0 },\r
227   { "petite",   33, 1, 1, 1, 0, 0 },\r
228   { "slim",     37, 2, 1, 0, 0, 0 },\r
229   { "small",    40, 2, 1, 0, 0, 0 },\r
230   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
231   { "middling", 49, 2, 0, 0, 0, 0 },\r
232   { "average",  54, 2, 0, 0, 0, 0 },\r
233   { "moderate", 58, 3, 0, 0, 0, 0 },\r
234   { "medium",   64, 3, 0, 0, 0, 0 },\r
235   { "bulky",    72, 3, 0, 0, 0, 0 },\r
236   { "large",    80, 3, 0, 0, 0, 0 },\r
237   { "big",      87, 3, 0, 0, 0, 0 },\r
238   { "huge",     95, 3, 0, 0, 0, 0 },\r
239   { "giant",    108, 3, 0, 0, 0, 0 },\r
240   { "colossal", 116, 4, 0, 0, 0, 0 },\r
241   { "titanic",  129, 4, 0, 0, 0, 0 },\r
242   { NULL, 0, 0, 0, 0, 0, 0 }\r
243 };\r
244 \r
245 #define MF(x) {x, {0, }, {0, }, 0}\r
246 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
247 {\r
248   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
249   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
250   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
251   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
252   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
253   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
254   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
255   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
256   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
257   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
258   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
259   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
260   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
261   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
262   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
263   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
264   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
265   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
266 };\r
267 \r
268 MyFont *font[NUM_SIZES][NUM_FONTS];\r
269 \r
270 typedef struct {\r
271   char *label;\r
272   int id;\r
273   HWND hwnd;\r
274   WNDPROC wndproc;\r
275 } MyButtonDesc;\r
276 \r
277 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
278 #define N_BUTTONS 5\r
279 \r
280 MyButtonDesc buttonDesc[N_BUTTONS] =\r
281 {\r
282   {"<<", IDM_ToStart, NULL, NULL},\r
283   {"<", IDM_Backward, NULL, NULL},\r
284   {"P", IDM_Pause, NULL, NULL},\r
285   {">", IDM_Forward, NULL, NULL},\r
286   {">>", IDM_ToEnd, NULL, NULL},\r
287 };\r
288 \r
289 int tinyLayout = 0, smallLayout = 0;\r
290 #define MENU_BAR_ITEMS 6\r
291 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
292   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
293   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
294 };\r
295 \r
296 \r
297 MySound sounds[(int)NSoundClasses];\r
298 MyTextAttribs textAttribs[(int)NColorClasses];\r
299 \r
300 MyColorizeAttribs colorizeAttribs[] = {\r
301   { (COLORREF)0, 0, "Shout Text" },\r
302   { (COLORREF)0, 0, "SShout/CShout" },\r
303   { (COLORREF)0, 0, "Channel 1 Text" },\r
304   { (COLORREF)0, 0, "Channel Text" },\r
305   { (COLORREF)0, 0, "Kibitz Text" },\r
306   { (COLORREF)0, 0, "Tell Text" },\r
307   { (COLORREF)0, 0, "Challenge Text" },\r
308   { (COLORREF)0, 0, "Request Text" },\r
309   { (COLORREF)0, 0, "Seek Text" },\r
310   { (COLORREF)0, 0, "Normal Text" },\r
311   { (COLORREF)0, 0, "None" }\r
312 };\r
313 \r
314 \r
315 \r
316 static char *commentTitle;\r
317 static char *commentText;\r
318 static int commentIndex;\r
319 static Boolean editComment = FALSE;\r
320 HWND commentDialog = NULL;\r
321 BOOLEAN commentDialogUp = FALSE;\r
322 static int commentX, commentY, commentH, commentW;\r
323 \r
324 static char *analysisTitle;\r
325 static char *analysisText;\r
326 HWND analysisDialog = NULL;\r
327 BOOLEAN analysisDialogUp = FALSE;\r
328 static int analysisX, analysisY, analysisH, analysisW;\r
329 \r
330 char errorTitle[MSG_SIZ];\r
331 char errorMessage[2*MSG_SIZ];\r
332 HWND errorDialog = NULL;\r
333 BOOLEAN moveErrorMessageUp = FALSE;\r
334 BOOLEAN consoleEcho = TRUE;\r
335 CHARFORMAT consoleCF;\r
336 COLORREF consoleBackgroundColor;\r
337 \r
338 char *programVersion;\r
339 \r
340 #define CPReal 1\r
341 #define CPComm 2\r
342 #define CPSock 3\r
343 #define CPRcmd 4\r
344 typedef int CPKind;\r
345 \r
346 typedef struct {\r
347   CPKind kind;\r
348   HANDLE hProcess;\r
349   DWORD pid;\r
350   HANDLE hTo;\r
351   HANDLE hFrom;\r
352   SOCKET sock;\r
353   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
354 } ChildProc;\r
355 \r
356 #define INPUT_SOURCE_BUF_SIZE 4096\r
357 \r
358 typedef struct _InputSource {\r
359   CPKind kind;\r
360   HANDLE hFile;\r
361   SOCKET sock;\r
362   int lineByLine;\r
363   HANDLE hThread;\r
364   DWORD id;\r
365   char buf[INPUT_SOURCE_BUF_SIZE];\r
366   char *next;\r
367   DWORD count;\r
368   int error;\r
369   InputCallback func;\r
370   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
371   VOIDSTAR closure;\r
372 } InputSource;\r
373 \r
374 InputSource *consoleInputSource;\r
375 \r
376 DCB dcb;\r
377 \r
378 /* forward */\r
379 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
380 VOID ConsoleCreate();\r
381 LRESULT CALLBACK\r
382   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
383 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
384 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
385 VOID ParseCommSettings(char *arg, DCB *dcb);\r
386 LRESULT CALLBACK\r
387   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
389 void ParseIcsTextMenu(char *icsTextMenuString);\r
390 VOID PopUpMoveDialog(char firstchar);\r
391 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
392 \r
393 /* [AS] */\r
394 int NewGameFRC();\r
395 int GameListOptions();\r
396 \r
397 HWND moveHistoryDialog = NULL;\r
398 BOOLEAN moveHistoryDialogUp = FALSE;\r
399 \r
400 WindowPlacement wpMoveHistory;\r
401 \r
402 HWND evalGraphDialog = NULL;\r
403 BOOLEAN evalGraphDialogUp = FALSE;\r
404 \r
405 WindowPlacement wpEvalGraph;\r
406 \r
407 HWND engineOutputDialog = NULL;\r
408 BOOLEAN engineOutputDialogUp = FALSE;\r
409 \r
410 WindowPlacement wpEngineOutput;\r
411 \r
412 VOID MoveHistoryPopUp();\r
413 VOID MoveHistoryPopDown();\r
414 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
415 BOOL MoveHistoryIsUp();\r
416 \r
417 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
418 VOID EvalGraphPopUp();\r
419 VOID EvalGraphPopDown();\r
420 BOOL EvalGraphIsUp();\r
421 \r
422 VOID EngineOutputPopUp();\r
423 VOID EngineOutputPopDown();\r
424 BOOL EngineOutputIsUp();\r
425 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
426 \r
427 VOID GothicPopUp(char *title, char up);\r
428 /*\r
429  * Setting "frozen" should disable all user input other than deleting\r
430  * the window.  We do this while engines are initializing themselves.\r
431  */\r
432 static int frozen = 0;\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];\r
434 void FreezeUI()\r
435 {\r
436   HMENU hmenu;\r
437   int i;\r
438 \r
439   if (frozen) return;\r
440   frozen = 1;\r
441   hmenu = GetMenu(hwndMain);\r
442   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
443     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
444   }\r
445   DrawMenuBar(hwndMain);\r
446 }\r
447 \r
448 /* Undo a FreezeUI */\r
449 void ThawUI()\r
450 {\r
451   HMENU hmenu;\r
452   int i;\r
453 \r
454   if (!frozen) return;\r
455   frozen = 0;\r
456   hmenu = GetMenu(hwndMain);\r
457   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
458     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
459   }\r
460   DrawMenuBar(hwndMain);\r
461 }\r
462 \r
463 /*---------------------------------------------------------------------------*\\r
464  *\r
465  * WinMain\r
466  *\r
467 \*---------------------------------------------------------------------------*/\r
468 \r
469 int APIENTRY\r
470 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
471         LPSTR lpCmdLine, int nCmdShow)\r
472 {\r
473   MSG msg;\r
474   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
475 \r
476   debugFP = stderr;\r
477 \r
478   LoadLibrary("RICHED32.DLL");\r
479   consoleCF.cbSize = sizeof(CHARFORMAT);\r
480 \r
481   if (!InitApplication(hInstance)) {\r
482     return (FALSE);\r
483   }\r
484   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
485     return (FALSE);\r
486   }\r
487 \r
488   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
489   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
490   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
491 \r
492   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
493 \r
494   while (GetMessage(&msg, /* message structure */\r
495                     NULL, /* handle of window receiving the message */\r
496                     0,    /* lowest message to examine */\r
497                     0))   /* highest message to examine */\r
498     {\r
499       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
500           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
501           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
502           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
503           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
504           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
505           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
506           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
507           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
508           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
509         TranslateMessage(&msg); /* Translates virtual key codes */\r
510         DispatchMessage(&msg);  /* Dispatches message to window */\r
511       }\r
512     }\r
513 \r
514 \r
515   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
516 }\r
517 \r
518 /*---------------------------------------------------------------------------*\\r
519  *\r
520  * Initialization functions\r
521  *\r
522 \*---------------------------------------------------------------------------*/\r
523 \r
524 BOOL\r
525 InitApplication(HINSTANCE hInstance)\r
526 {\r
527   WNDCLASS wc;\r
528 \r
529   /* Fill in window class structure with parameters that describe the */\r
530   /* main window. */\r
531 \r
532   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
533   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
534   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
535   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
536   wc.hInstance     = hInstance;         /* Owner of this class */\r
537   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
538   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
539   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
540   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
541   wc.lpszClassName = szAppName;                 /* Name to register as */\r
542 \r
543   /* Register the window class and return success/failure code. */\r
544   if (!RegisterClass(&wc)) return FALSE;\r
545 \r
546   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
547   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
548   wc.cbClsExtra    = 0;\r
549   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
550   wc.hInstance     = hInstance;\r
551   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
552   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
553   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
554   wc.lpszMenuName  = NULL;\r
555   wc.lpszClassName = szConsoleName;\r
556 \r
557   if (!RegisterClass(&wc)) return FALSE;\r
558   return TRUE;\r
559 }\r
560 \r
561 \r
562 /* Set by InitInstance, used by EnsureOnScreen */\r
563 int screenHeight, screenWidth;\r
564 \r
565 void\r
566 EnsureOnScreen(int *x, int *y)\r
567 {\r
568   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
569   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
570   if (*x > screenWidth - 32) *x = 0;\r
571   if (*y > screenHeight - 32) *y = 0;\r
572   if (*x < 10) *x = 10;\r
573   if (*y < gap) *y = gap;\r
574 }\r
575 \r
576 BOOL\r
577 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
578 {\r
579   HWND hwnd; /* Main window handle. */\r
580   int ibs;\r
581   WINDOWPLACEMENT wp;\r
582   char *filepart;\r
583 \r
584   hInst = hInstance;    /* Store instance handle in our global variable */\r
585 \r
586   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
587     *filepart = NULLCHAR;\r
588   } else {\r
589     GetCurrentDirectory(MSG_SIZ, installDir);\r
590   }\r
591   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
592   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
593   if (appData.debugMode) {\r
594     debugFP = fopen(appData.nameOfDebugFile, "w");\r
595     setbuf(debugFP, NULL);\r
596   }\r
597 \r
598   InitBackEnd1();\r
599 \r
600   InitEngineUCI( installDir, &first );\r
601   InitEngineUCI( installDir, &second );\r
602 \r
603   /* Create a main window for this application instance. */\r
604   hwnd = CreateWindow(szAppName, szTitle,\r
605                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
606                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
607                       NULL, NULL, hInstance, NULL);\r
608   hwndMain = hwnd;\r
609 \r
610   /* If window could not be created, return "failure" */\r
611   if (!hwnd) {\r
612     return (FALSE);\r
613   }\r
614 \r
615   iconWhite = LoadIcon(hInstance, "icon_white");\r
616   iconBlack = LoadIcon(hInstance, "icon_black");\r
617   iconCurrent = iconWhite;\r
618   InitDrawingColors();\r
619   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
620   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
621   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
622     /* Compute window size for each board size, and use the largest\r
623        size that fits on this screen as the default. */\r
624     InitDrawingSizes((BoardSize)ibs, 0);\r
625     if (boardSize == (BoardSize)-1 &&\r
626         winHeight <= screenHeight\r
627            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
628         && winWidth <= screenWidth) {\r
629       boardSize = (BoardSize)ibs;\r
630     }\r
631   }\r
632   InitDrawingSizes(boardSize, 0);\r
633   InitMenuChecks();\r
634   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
635 \r
636   /* [AS] Load textures if specified */\r
637   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
638   \r
639   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
640       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
641       liteBackTextureMode = appData.liteBackTextureMode;\r
642 \r
643       if (liteBackTexture == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
645       }\r
646   }\r
647   \r
648   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
649       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
650       darkBackTextureMode = appData.darkBackTextureMode;\r
651 \r
652       if (darkBackTexture == NULL && appData.debugMode) {\r
653           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
654       }\r
655   }\r
656 \r
657   mysrandom( (unsigned) time(NULL) );\r
658 \r
659   /* Make a console window if needed */\r
660   if (appData.icsActive) {\r
661     ConsoleCreate();\r
662   }\r
663 \r
664   /* [AS] Restore layout */\r
665   if( wpMoveHistory.visible ) {\r
666       MoveHistoryPopUp();\r
667   }\r
668 \r
669   if( wpEvalGraph.visible ) {\r
670       EvalGraphPopUp();\r
671   }\r
672 \r
673   if( wpEngineOutput.visible ) {\r
674       EngineOutputPopUp();\r
675   }\r
676 \r
677   InitBackEnd2();\r
678 \r
679   /* Make the window visible; update its client area; and return "success" */\r
680   EnsureOnScreen(&boardX, &boardY);\r
681   wp.length = sizeof(WINDOWPLACEMENT);\r
682   wp.flags = 0;\r
683   wp.showCmd = nCmdShow;\r
684   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
685   wp.rcNormalPosition.left = boardX;\r
686   wp.rcNormalPosition.right = boardX + winWidth;\r
687   wp.rcNormalPosition.top = boardY;\r
688   wp.rcNormalPosition.bottom = boardY + winHeight;\r
689   SetWindowPlacement(hwndMain, &wp);\r
690 \r
691   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
692                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
693 \r
694   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
695   if( gameInfo.variant != VariantFischeRandom ) {\r
696       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
697   }\r
698 \r
699   if (hwndConsole) {\r
700 #if AOT_CONSOLE\r
701     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
702                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
703 #endif\r
704     ShowWindow(hwndConsole, nCmdShow);\r
705   }\r
706   UpdateWindow(hwnd);\r
707 \r
708   return TRUE;\r
709 \r
710 }\r
711 \r
712 \r
713 typedef enum {\r
714   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
715   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
716   ArgSettingsFilename\r
717 } ArgType;\r
718 \r
719 typedef struct {\r
720   char *argName;\r
721   ArgType argType;\r
722   /***\r
723   union {\r
724     String *pString;       // ArgString\r
725     int *pInt;             // ArgInt\r
726     float *pFloat;         // ArgFloat\r
727     Boolean *pBoolean;     // ArgBoolean\r
728     COLORREF *pColor;      // ArgColor\r
729     ColorClass cc;         // ArgAttribs\r
730     String *pFilename;     // ArgFilename\r
731     BoardSize *pBoardSize; // ArgBoardSize\r
732     int whichFont;         // ArgFont\r
733     DCB *pDCB;             // ArgCommSettings\r
734     String *pFilename;     // ArgSettingsFilename\r
735   } argLoc;\r
736   ***/\r
737   LPVOID argLoc;\r
738   BOOL save;\r
739 } ArgDescriptor;\r
740 \r
741 int junk;\r
742 ArgDescriptor argDescriptors[] = {\r
743   /* positional arguments */\r
744   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
745   { "", ArgNone, NULL },\r
746   /* keyword arguments */\r
747   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
748   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
749   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
750   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
751   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
752   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
753   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
754   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
755   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
756   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
757   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
758   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
759   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
760   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
761   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
762   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
763   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
764   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
765     FALSE },\r
766   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
767     FALSE },\r
768   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
769     FALSE },\r
770   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
771   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
772     FALSE },\r
773   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
774   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
775   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
776   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
779   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
780   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
783   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
784   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
785   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
786   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
787   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
788   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
789   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
790   /*!!bitmapDirectory?*/\r
791   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
792   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
793   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
794   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
795   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
796   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
797   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
798   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
799   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
800   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
801   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
802   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
803   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
804   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
805   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
806   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
807   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
808   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
809   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
810   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
811   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
814   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
815   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
816   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
817   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
820   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
821   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
822   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
823   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
824   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
825   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
826   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
827   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
828   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
829   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
830   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
831   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
833   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
834   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
835   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
836   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
837   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
838   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
839   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
840   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
841   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
842   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
843   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
844   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
845   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
846   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
847   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
848   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
849   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
850   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
851   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
852   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
853   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
854   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
855   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
856   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
857   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
858   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
859   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
860   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
861   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
862   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
863   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
864   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
865   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
866   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
867   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
868   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
869   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
870   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
871   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
873   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
874   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
875   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
877   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
878   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
879   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
881   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
882   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
883   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
885     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
886   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
887   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
888   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
889   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
890   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
891   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
892   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
893   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
894     TRUE }, /* must come after all fonts */\r
895   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
896   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
897     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
898   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
899   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
900   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
901   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
903   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
904   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
905   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
907   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
908   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
909   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
910   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
911   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
912   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
913   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
915   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
916   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
917   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
918   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
919   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
920   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
921   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
923   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
924   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
925   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926 #if 0\r
927   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
928   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
929 #endif\r
930   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
931   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
932   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
933   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
935   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
936   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
937   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
939   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
940   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
941   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
943   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
944   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
945   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
947   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
948   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
949   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
950   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
951   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
952   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
953   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
954   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
955   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
956   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
957   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
959   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
960   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
961   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
962   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
964   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
965   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
966   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
967   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
969   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
970   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
971   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
972   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
973   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
975   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
976   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
977   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
978   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
979   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
980   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
981   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
982   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
984   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
985   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
986   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
987   { "highlightLastMove", ArgBoolean,\r
988     (LPVOID) &appData.highlightLastMove, TRUE },\r
989   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
990   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
991   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "highlightDragging", ArgBoolean,\r
993     (LPVOID) &appData.highlightDragging, TRUE },\r
994   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
995   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
996   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
998   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
999   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1000   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1001   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1002   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1003   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1004   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1005   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1006   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1007   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1008   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1009   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1010   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1011   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1012   { "soundShout", ArgFilename,\r
1013     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1014   { "soundSShout", ArgFilename,\r
1015     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1016   { "soundChannel1", ArgFilename,\r
1017     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1018   { "soundChannel", ArgFilename,\r
1019     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1020   { "soundKibitz", ArgFilename,\r
1021     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1022   { "soundTell", ArgFilename,\r
1023     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1024   { "soundChallenge", ArgFilename,\r
1025     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1026   { "soundRequest", ArgFilename,\r
1027     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1028   { "soundSeek", ArgFilename,\r
1029     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1030   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1031   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1032   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1033   { "soundIcsLoss", ArgFilename, \r
1034     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1035   { "soundIcsDraw", ArgFilename, \r
1036     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1037   { "soundIcsUnfinished", ArgFilename, \r
1038     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1039   { "soundIcsAlarm", ArgFilename, \r
1040     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1041   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1042   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1043   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "reuseChessPrograms", ArgBoolean,\r
1046     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1047   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1048   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1049   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1052   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1053   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1054   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1055   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1056   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1057   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1058   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1059   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1060   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1061   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1062   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1063   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1064   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1065   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1066   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1067   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1068   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1069   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1070   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1071   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1072   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1073   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1074   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1075   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1076   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1077   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1078   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1079   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1080   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1081   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1082   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1083   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1084     TRUE },\r
1085   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1086     TRUE },\r
1087   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1088   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1089   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1090   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1091   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1092   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1093   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1094   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1095   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   /* [AS] New features */\r
1097   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1098   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1099   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1100   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1101   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1102   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1103   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1104   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1105   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1106   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1107   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1108   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1109   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1110   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1111   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1112   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1113   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1114   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1115   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1116   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1117   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1118   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1119   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1120   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1121   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1122   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1123   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1124   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1125   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1126   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1127   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1128   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1129   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1130   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1131   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1132   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1133   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1134   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1135   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1136   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1137   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1138   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1139   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1140   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1141   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1142   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1143   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1144   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1145 \r
1146   /* [AS] Layout stuff */\r
1147   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1148   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1149   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1150   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1151   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1152 \r
1153   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1154   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1155   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1156   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1157   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1158 \r
1159   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1160   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1161   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1162   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1163   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1164 \r
1165   /* [HGM] board-size, adjudication and misc. options */\r
1166   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1167   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1168   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1169   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1170   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1171   { "flipBlack", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1172   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1173   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1174   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1175   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1176   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1177   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1178   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1179   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1180 \r
1181 #ifdef ZIPPY\r
1182   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1183   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1184   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1185   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1186   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1187   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1188   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1189   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1190   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1191   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1192   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1193   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1194   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1195     FALSE },\r
1196   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1197   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1198   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1199   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1200   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1201   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1202   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1203     FALSE },\r
1204   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1205   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1206   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1207   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1208   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1209   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1210   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1211   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1212   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1213   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1214   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1215   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1216   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1217   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1218   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1219   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1220   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1221   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1222 #endif\r
1223   { NULL, ArgNone, NULL, FALSE }\r
1224 };\r
1225 \r
1226 \r
1227 /* Kludge for indirection files on command line */\r
1228 char* lastIndirectionFilename;\r
1229 ArgDescriptor argDescriptorIndirection =\r
1230 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1231 \r
1232 \r
1233 VOID\r
1234 ExitArgError(char *msg, char *badArg)\r
1235 {\r
1236   char buf[MSG_SIZ];\r
1237 \r
1238   sprintf(buf, "%s %s", msg, badArg);\r
1239   DisplayFatalError(buf, 0, 2);\r
1240   exit(2);\r
1241 }\r
1242 \r
1243 /* Command line font name parser.  NULL name means do nothing.\r
1244    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1245    For backward compatibility, syntax without the colon is also\r
1246    accepted, but font names with digits in them won't work in that case.\r
1247 */\r
1248 VOID\r
1249 ParseFontName(char *name, MyFontParams *mfp)\r
1250 {\r
1251   char *p, *q;\r
1252   if (name == NULL) return;\r
1253   p = name;\r
1254   q = strchr(p, ':');\r
1255   if (q) {\r
1256     if (q - p >= sizeof(mfp->faceName))\r
1257       ExitArgError("Font name too long:", name);\r
1258     memcpy(mfp->faceName, p, q - p);\r
1259     mfp->faceName[q - p] = NULLCHAR;\r
1260     p = q + 1;\r
1261   } else {\r
1262     q = mfp->faceName;\r
1263     while (*p && !isdigit(*p)) {\r
1264       *q++ = *p++;\r
1265       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1266         ExitArgError("Font name too long:", name);\r
1267     }\r
1268     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1269     *q = NULLCHAR;\r
1270   }\r
1271   if (!*p) ExitArgError("Font point size missing:", name);\r
1272   mfp->pointSize = (float) atof(p);\r
1273   mfp->bold = (strchr(p, 'b') != NULL);\r
1274   mfp->italic = (strchr(p, 'i') != NULL);\r
1275   mfp->underline = (strchr(p, 'u') != NULL);\r
1276   mfp->strikeout = (strchr(p, 's') != NULL);\r
1277 }\r
1278 \r
1279 /* Color name parser.\r
1280    X version accepts X color names, but this one\r
1281    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1282 COLORREF\r
1283 ParseColorName(char *name)\r
1284 {\r
1285   int red, green, blue, count;\r
1286   char buf[MSG_SIZ];\r
1287 \r
1288   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1289   if (count != 3) {\r
1290     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1291       &red, &green, &blue);\r
1292   }\r
1293   if (count != 3) {\r
1294     sprintf(buf, "Can't parse color name %s", name);\r
1295     DisplayError(buf, 0);\r
1296     return RGB(0, 0, 0);\r
1297   }\r
1298   return PALETTERGB(red, green, blue);\r
1299 }\r
1300 \r
1301 \r
1302 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1303 {\r
1304   char *e = argValue;\r
1305   int eff = 0;\r
1306 \r
1307   while (*e) {\r
1308     if (*e == 'b')      eff |= CFE_BOLD;\r
1309     else if (*e == 'i') eff |= CFE_ITALIC;\r
1310     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1311     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1312     else if (*e == '#' || isdigit(*e)) break;\r
1313     e++;\r
1314   }\r
1315   *effects = eff;\r
1316   *color   = ParseColorName(e);\r
1317 }\r
1318 \r
1319 \r
1320 BoardSize\r
1321 ParseBoardSize(char *name)\r
1322 {\r
1323   BoardSize bs = SizeTiny;\r
1324   while (sizeInfo[bs].name != NULL) {\r
1325     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1326     bs++;\r
1327   }\r
1328   ExitArgError("Unrecognized board size value", name);\r
1329   return bs; /* not reached */\r
1330 }\r
1331 \r
1332 \r
1333 char\r
1334 StringGet(void *getClosure)\r
1335 {\r
1336   char **p = (char **) getClosure;\r
1337   return *((*p)++);\r
1338 }\r
1339 \r
1340 char\r
1341 FileGet(void *getClosure)\r
1342 {\r
1343   int c;\r
1344   FILE* f = (FILE*) getClosure;\r
1345 \r
1346   c = getc(f);\r
1347   if (c == EOF)\r
1348     return NULLCHAR;\r
1349   else\r
1350     return (char) c;\r
1351 }\r
1352 \r
1353 /* Parse settings file named "name". If file found, return the\r
1354    full name in fullname and return TRUE; else return FALSE */\r
1355 BOOLEAN\r
1356 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1357 {\r
1358   char *dummy;\r
1359   FILE *f;\r
1360 \r
1361   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1362     f = fopen(fullname, "r");\r
1363     if (f != NULL) {\r
1364       ParseArgs(FileGet, f);\r
1365       fclose(f);\r
1366       return TRUE;\r
1367     }\r
1368   }\r
1369   return FALSE;\r
1370 }\r
1371 \r
1372 VOID\r
1373 ParseArgs(GetFunc get, void *cl)\r
1374 {\r
1375   char argName[ARG_MAX];\r
1376   char argValue[ARG_MAX];\r
1377   ArgDescriptor *ad;\r
1378   char start;\r
1379   char *q;\r
1380   int i, octval;\r
1381   char ch;\r
1382   int posarg = 0;\r
1383 \r
1384   ch = get(cl);\r
1385   for (;;) {\r
1386     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1387     if (ch == NULLCHAR) break;\r
1388     if (ch == ';') {\r
1389       /* Comment to end of line */\r
1390       ch = get(cl);\r
1391       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1392       continue;\r
1393     } else if (ch == '/' || ch == '-') {\r
1394       /* Switch */\r
1395       q = argName;\r
1396       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1397              ch != '\n' && ch != '\t') {\r
1398         *q++ = ch;\r
1399         ch = get(cl);\r
1400       }\r
1401       *q = NULLCHAR;\r
1402 \r
1403       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1404         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1405 \r
1406       if (ad->argName == NULL)\r
1407         ExitArgError("Unrecognized argument", argName);\r
1408 \r
1409     } else if (ch == '@') {\r
1410       /* Indirection file */\r
1411       ad = &argDescriptorIndirection;\r
1412       ch = get(cl);\r
1413     } else {\r
1414       /* Positional argument */\r
1415       ad = &argDescriptors[posarg++];\r
1416       strcpy(argName, ad->argName);\r
1417     }\r
1418 \r
1419     if (ad->argType == ArgTrue) {\r
1420       *(Boolean *) ad->argLoc = TRUE;\r
1421       continue;\r
1422     }\r
1423     if (ad->argType == ArgFalse) {\r
1424       *(Boolean *) ad->argLoc = FALSE;\r
1425       continue;\r
1426     }\r
1427 \r
1428     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1429     if (ch == NULLCHAR || ch == '\n') {\r
1430       ExitArgError("No value provided for argument", argName);\r
1431     }\r
1432     q = argValue;\r
1433     if (ch == '{') {\r
1434       // Quoting with { }.  No characters have to (or can) be escaped.\r
1435       // Thus the string cannot contain a '}' character.\r
1436       start = ch;\r
1437       ch = get(cl);\r
1438       while (start) {\r
1439         switch (ch) {\r
1440         case NULLCHAR:\r
1441           start = NULLCHAR;\r
1442           break;\r
1443           \r
1444         case '}':\r
1445           ch = get(cl);\r
1446           start = NULLCHAR;\r
1447           break;\r
1448 \r
1449         default:\r
1450           *q++ = ch;\r
1451           ch = get(cl);\r
1452           break;\r
1453         }\r
1454       }   \r
1455     } else if (ch == '\'' || ch == '"') {\r
1456       // Quoting with ' ' or " ", with \ as escape character.\r
1457       // Inconvenient for long strings that may contain Windows filenames.\r
1458       start = ch;\r
1459       ch = get(cl);\r
1460       while (start) {\r
1461         switch (ch) {\r
1462         case NULLCHAR:\r
1463           start = NULLCHAR;\r
1464           break;\r
1465 \r
1466         default:\r
1467         not_special:\r
1468           *q++ = ch;\r
1469           ch = get(cl);\r
1470           break;\r
1471 \r
1472         case '\'':\r
1473         case '\"':\r
1474           if (ch == start) {\r
1475             ch = get(cl);\r
1476             start = NULLCHAR;\r
1477             break;\r
1478           } else {\r
1479             goto not_special;\r
1480           }\r
1481 \r
1482         case '\\':\r
1483           if (ad->argType == ArgFilename\r
1484               || ad->argType == ArgSettingsFilename) {\r
1485               goto not_special;\r
1486           }\r
1487           ch = get(cl);\r
1488           switch (ch) {\r
1489           case NULLCHAR:\r
1490             ExitArgError("Incomplete \\ escape in value for", argName);\r
1491             break;\r
1492           case 'n':\r
1493             *q++ = '\n';\r
1494             ch = get(cl);\r
1495             break;\r
1496           case 'r':\r
1497             *q++ = '\r';\r
1498             ch = get(cl);\r
1499             break;\r
1500           case 't':\r
1501             *q++ = '\t';\r
1502             ch = get(cl);\r
1503             break;\r
1504           case 'b':\r
1505             *q++ = '\b';\r
1506             ch = get(cl);\r
1507             break;\r
1508           case 'f':\r
1509             *q++ = '\f';\r
1510             ch = get(cl);\r
1511             break;\r
1512           default:\r
1513             octval = 0;\r
1514             for (i = 0; i < 3; i++) {\r
1515               if (ch >= '0' && ch <= '7') {\r
1516                 octval = octval*8 + (ch - '0');\r
1517                 ch = get(cl);\r
1518               } else {\r
1519                 break;\r
1520               }\r
1521             }\r
1522             if (i > 0) {\r
1523               *q++ = (char) octval;\r
1524             } else {\r
1525               *q++ = ch;\r
1526               ch = get(cl);\r
1527             }\r
1528             break;\r
1529           }\r
1530           break;\r
1531         }\r
1532       }\r
1533     } else {\r
1534       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1535         *q++ = ch;\r
1536         ch = get(cl);\r
1537       }\r
1538     }\r
1539     *q = NULLCHAR;\r
1540 \r
1541     switch (ad->argType) {\r
1542     case ArgInt:\r
1543       *(int *) ad->argLoc = atoi(argValue);\r
1544       break;\r
1545 \r
1546     case ArgFloat:\r
1547       *(float *) ad->argLoc = (float) atof(argValue);\r
1548       break;\r
1549 \r
1550     case ArgString:\r
1551     case ArgFilename:\r
1552       *(char **) ad->argLoc = strdup(argValue);\r
1553       break;\r
1554 \r
1555     case ArgSettingsFilename:\r
1556       {\r
1557         char fullname[MSG_SIZ];\r
1558         if (ParseSettingsFile(argValue, fullname)) {\r
1559           if (ad->argLoc != NULL) {\r
1560             *(char **) ad->argLoc = strdup(fullname);\r
1561           }\r
1562         } else {\r
1563           if (ad->argLoc != NULL) {\r
1564           } else {\r
1565             ExitArgError("Failed to open indirection file", argValue);\r
1566           }\r
1567         }\r
1568       }\r
1569       break;\r
1570 \r
1571     case ArgBoolean:\r
1572       switch (argValue[0]) {\r
1573       case 't':\r
1574       case 'T':\r
1575         *(Boolean *) ad->argLoc = TRUE;\r
1576         break;\r
1577       case 'f':\r
1578       case 'F':\r
1579         *(Boolean *) ad->argLoc = FALSE;\r
1580         break;\r
1581       default:\r
1582         ExitArgError("Unrecognized boolean argument value", argValue);\r
1583         break;\r
1584       }\r
1585       break;\r
1586 \r
1587     case ArgColor:\r
1588       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1589       break;\r
1590 \r
1591     case ArgAttribs: {\r
1592       ColorClass cc = (ColorClass)ad->argLoc;\r
1593       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1594       }\r
1595       break;\r
1596       \r
1597     case ArgBoardSize:\r
1598       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1599       break;\r
1600 \r
1601     case ArgFont:\r
1602       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1603       break;\r
1604 \r
1605     case ArgCommSettings:\r
1606       ParseCommSettings(argValue, &dcb);\r
1607       break;\r
1608 \r
1609     case ArgNone:\r
1610       ExitArgError("Unrecognized argument", argValue);\r
1611       break;\r
1612     }\r
1613   }\r
1614 }\r
1615 \r
1616 VOID\r
1617 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1618 {\r
1619   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1620   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1621   DeleteDC(hdc);\r
1622   lf->lfWidth = 0;\r
1623   lf->lfEscapement = 0;\r
1624   lf->lfOrientation = 0;\r
1625   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1626   lf->lfItalic = mfp->italic;\r
1627   lf->lfUnderline = mfp->underline;\r
1628   lf->lfStrikeOut = mfp->strikeout;\r
1629   lf->lfCharSet = DEFAULT_CHARSET;\r
1630   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1631   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1632   lf->lfQuality = DEFAULT_QUALITY;\r
1633   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1634   strcpy(lf->lfFaceName, mfp->faceName);\r
1635 }\r
1636 \r
1637 VOID\r
1638 CreateFontInMF(MyFont *mf)\r
1639 {\r
1640   LFfromMFP(&mf->lf, &mf->mfp);\r
1641   if (mf->hf) DeleteObject(mf->hf);\r
1642   mf->hf = CreateFontIndirect(&mf->lf);\r
1643 }\r
1644 \r
1645 VOID\r
1646 SetDefaultTextAttribs()\r
1647 {\r
1648   ColorClass cc;\r
1649   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1650     ParseAttribs(&textAttribs[cc].color, \r
1651                  &textAttribs[cc].effects, \r
1652                  defaultTextAttribs[cc]);\r
1653   }\r
1654 }\r
1655 \r
1656 VOID\r
1657 SetDefaultSounds()\r
1658 {\r
1659   ColorClass cc;\r
1660   SoundClass sc;\r
1661   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1662     textAttribs[cc].sound.name = strdup("");\r
1663     textAttribs[cc].sound.data = NULL;\r
1664   }\r
1665   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1666     sounds[sc].name = strdup("");\r
1667     sounds[sc].data = NULL;\r
1668   }\r
1669   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1670 }\r
1671 \r
1672 VOID\r
1673 LoadAllSounds()\r
1674 {\r
1675   ColorClass cc;\r
1676   SoundClass sc;\r
1677   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1678     MyLoadSound(&textAttribs[cc].sound);\r
1679   }\r
1680   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1681     MyLoadSound(&sounds[sc]);\r
1682   }\r
1683 }\r
1684 \r
1685 VOID\r
1686 InitAppData(LPSTR lpCmdLine)\r
1687 {\r
1688   int i, j;\r
1689   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1690   char *dummy, *p;\r
1691 \r
1692   programName = szAppName;\r
1693 \r
1694   /* Initialize to defaults */\r
1695   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1696   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1697   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1698   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1699   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1700   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1701   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1702   SetDefaultTextAttribs();\r
1703   SetDefaultSounds();\r
1704   appData.movesPerSession = MOVES_PER_SESSION;\r
1705   appData.initString = INIT_STRING;\r
1706   appData.secondInitString = INIT_STRING;\r
1707   appData.firstComputerString = COMPUTER_STRING;\r
1708   appData.secondComputerString = COMPUTER_STRING;\r
1709   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1710   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1711   appData.firstPlaysBlack = FALSE;\r
1712   appData.noChessProgram = FALSE;\r
1713   chessProgram = FALSE;\r
1714   appData.firstHost = FIRST_HOST;\r
1715   appData.secondHost = SECOND_HOST;\r
1716   appData.firstDirectory = FIRST_DIRECTORY;\r
1717   appData.secondDirectory = SECOND_DIRECTORY;\r
1718   appData.bitmapDirectory = "";\r
1719   appData.remoteShell = REMOTE_SHELL;\r
1720   appData.remoteUser = "";\r
1721   appData.timeDelay = TIME_DELAY;\r
1722   appData.timeControl = TIME_CONTROL;\r
1723   appData.timeIncrement = TIME_INCREMENT;\r
1724   appData.icsActive = FALSE;\r
1725   appData.icsHost = "";\r
1726   appData.icsPort = ICS_PORT;\r
1727   appData.icsCommPort = ICS_COMM_PORT;\r
1728   appData.icsLogon = ICS_LOGON;\r
1729   appData.icsHelper = "";\r
1730   appData.useTelnet = FALSE;\r
1731   appData.telnetProgram = TELNET_PROGRAM;\r
1732   appData.gateway = "";\r
1733   appData.loadGameFile = "";\r
1734   appData.loadGameIndex = 0;\r
1735   appData.saveGameFile = "";\r
1736   appData.autoSaveGames = FALSE;\r
1737   appData.loadPositionFile = "";\r
1738   appData.loadPositionIndex = 1;\r
1739   appData.savePositionFile = "";\r
1740   appData.matchMode = FALSE;\r
1741   appData.matchGames = 0;\r
1742   appData.monoMode = FALSE;\r
1743   appData.debugMode = FALSE;\r
1744   appData.clockMode = TRUE;\r
1745   boardSize = (BoardSize) -1; /* determine by screen size */\r
1746   appData.Iconic = FALSE; /*unused*/\r
1747   appData.searchTime = "";\r
1748   appData.searchDepth = 0;\r
1749   appData.showCoords = FALSE;\r
1750   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1751   appData.autoCallFlag = FALSE;\r
1752   appData.flipView = FALSE;\r
1753   appData.autoFlipView = TRUE;\r
1754   appData.cmailGameName = "";\r
1755   appData.alwaysPromoteToQueen = FALSE;\r
1756   appData.oldSaveStyle = FALSE;\r
1757   appData.quietPlay = FALSE;\r
1758   appData.showThinking = FALSE;\r
1759   appData.ponderNextMove = TRUE;\r
1760   appData.periodicUpdates = TRUE;\r
1761   appData.popupExitMessage = TRUE;\r
1762   appData.popupMoveErrors = FALSE;\r
1763   appData.autoObserve = FALSE;\r
1764   appData.autoComment = FALSE;\r
1765   appData.animate = TRUE;\r
1766   appData.animSpeed = 10;\r
1767   appData.animateDragging = TRUE;\r
1768   appData.highlightLastMove = TRUE;\r
1769   appData.getMoveList = TRUE;\r
1770   appData.testLegality = TRUE;\r
1771   appData.premove = TRUE;\r
1772   appData.premoveWhite = FALSE;\r
1773   appData.premoveWhiteText = "";\r
1774   appData.premoveBlack = FALSE;\r
1775   appData.premoveBlackText = "";\r
1776   appData.icsAlarm = TRUE;\r
1777   appData.icsAlarmTime = 5000;\r
1778   appData.autoRaiseBoard = TRUE;\r
1779   appData.localLineEditing = TRUE;\r
1780   appData.colorize = TRUE;\r
1781   appData.reuseFirst = TRUE;\r
1782   appData.reuseSecond = TRUE;\r
1783   appData.blindfold = FALSE;\r
1784   dcb.DCBlength = sizeof(DCB);\r
1785   dcb.BaudRate = 9600;\r
1786   dcb.fBinary = TRUE;\r
1787   dcb.fParity = FALSE;\r
1788   dcb.fOutxCtsFlow = FALSE;\r
1789   dcb.fOutxDsrFlow = FALSE;\r
1790   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1791   dcb.fDsrSensitivity = FALSE;\r
1792   dcb.fTXContinueOnXoff = TRUE;\r
1793   dcb.fOutX = FALSE;\r
1794   dcb.fInX = FALSE;\r
1795   dcb.fNull = FALSE;\r
1796   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1797   dcb.fAbortOnError = FALSE;\r
1798   dcb.wReserved = 0;\r
1799   dcb.ByteSize = 7;\r
1800   dcb.Parity = SPACEPARITY;\r
1801   dcb.StopBits = ONESTOPBIT;\r
1802   settingsFileName = SETTINGS_FILE;\r
1803   saveSettingsOnExit = TRUE;\r
1804   boardX = CW_USEDEFAULT;\r
1805   boardY = CW_USEDEFAULT;\r
1806   consoleX = CW_USEDEFAULT; \r
1807   consoleY = CW_USEDEFAULT; \r
1808   consoleW = CW_USEDEFAULT;\r
1809   consoleH = CW_USEDEFAULT;\r
1810   analysisX = CW_USEDEFAULT; \r
1811   analysisY = CW_USEDEFAULT; \r
1812   analysisW = CW_USEDEFAULT;\r
1813   analysisH = CW_USEDEFAULT;\r
1814   commentX = CW_USEDEFAULT; \r
1815   commentY = CW_USEDEFAULT; \r
1816   commentW = CW_USEDEFAULT;\r
1817   commentH = CW_USEDEFAULT;\r
1818   editTagsX = CW_USEDEFAULT; \r
1819   editTagsY = CW_USEDEFAULT; \r
1820   editTagsW = CW_USEDEFAULT;\r
1821   editTagsH = CW_USEDEFAULT;\r
1822   gameListX = CW_USEDEFAULT; \r
1823   gameListY = CW_USEDEFAULT; \r
1824   gameListW = CW_USEDEFAULT;\r
1825   gameListH = CW_USEDEFAULT;\r
1826   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1827   icsNames = ICS_NAMES;\r
1828   firstChessProgramNames = FCP_NAMES;\r
1829   secondChessProgramNames = SCP_NAMES;\r
1830   appData.initialMode = "";\r
1831   appData.variant = "normal";\r
1832   appData.firstProtocolVersion = PROTOVER;\r
1833   appData.secondProtocolVersion = PROTOVER;\r
1834   appData.showButtonBar = TRUE;\r
1835 \r
1836    /* [AS] New properties (see comments in header file) */\r
1837   appData.firstScoreIsAbsolute = FALSE;\r
1838   appData.secondScoreIsAbsolute = FALSE;\r
1839   appData.saveExtendedInfoInPGN = FALSE;\r
1840   appData.hideThinkingFromHuman = FALSE;\r
1841   appData.liteBackTextureFile = "";\r
1842   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1843   appData.darkBackTextureFile = "";\r
1844   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1845   appData.renderPiecesWithFont = "";\r
1846   appData.fontToPieceTable = "";\r
1847   appData.fontBackColorWhite = 0;\r
1848   appData.fontForeColorWhite = 0;\r
1849   appData.fontBackColorBlack = 0;\r
1850   appData.fontForeColorBlack = 0;\r
1851   appData.fontPieceSize = 80;\r
1852   appData.overrideLineGap = 1;\r
1853   appData.adjudicateLossThreshold = 0;\r
1854   appData.delayBeforeQuit = 0;\r
1855   appData.delayAfterQuit = 0;\r
1856   appData.nameOfDebugFile = "winboard.debug";\r
1857   appData.pgnEventHeader = "Computer Chess Game";\r
1858   appData.defaultFrcPosition = -1;\r
1859   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1860   appData.saveOutOfBookInfo = TRUE;\r
1861   appData.showEvalInMoveHistory = TRUE;\r
1862   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1863   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1864   appData.highlightMoveWithArrow = FALSE;\r
1865   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1866   appData.useStickyWindows = TRUE;\r
1867   appData.adjudicateDrawMoves = 0;\r
1868   appData.autoDisplayComment = TRUE;\r
1869   appData.autoDisplayTags = TRUE;\r
1870   appData.firstIsUCI = FALSE;\r
1871   appData.secondIsUCI = FALSE;\r
1872   appData.firstHasOwnBookUCI = TRUE;\r
1873   appData.secondHasOwnBookUCI = TRUE;\r
1874   appData.polyglotDir = "";\r
1875   appData.usePolyglotBook = FALSE;\r
1876   appData.polyglotBook = "";\r
1877   appData.defaultHashSize = 64;\r
1878   appData.defaultCacheSizeEGTB = 4;\r
1879   appData.defaultPathEGTB = "c:\\egtb";\r
1880 \r
1881   InitWindowPlacement( &wpMoveHistory );\r
1882   InitWindowPlacement( &wpEvalGraph );\r
1883   InitWindowPlacement( &wpEngineOutput );\r
1884 \r
1885   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1886   appData.NrFiles      = -1;\r
1887   appData.NrRanks      = -1;\r
1888   appData.holdingsSize = -1;\r
1889   appData.testClaims   = FALSE;\r
1890   appData.checkMates   = FALSE;\r
1891   appData.materialDraws= FALSE;\r
1892   appData.trivialDraws = FALSE;\r
1893   appData.ruleMoves    = 51;\r
1894   appData.drawRepeats  = 6;\r
1895   appData.matchPause   = 10000;\r
1896   appData.alphaRank    = FALSE;\r
1897   appData.allWhite     = FALSE;\r
1898   appData.upsideDown   = FALSE;\r
1899 \r
1900 #ifdef ZIPPY\r
1901   appData.zippyTalk = ZIPPY_TALK;\r
1902   appData.zippyPlay = ZIPPY_PLAY;\r
1903   appData.zippyLines = ZIPPY_LINES;\r
1904   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1905   appData.zippyPassword = ZIPPY_PASSWORD;\r
1906   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1907   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1908   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1909   appData.zippyUseI = ZIPPY_USE_I;\r
1910   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1911   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1912   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1913   appData.zippyGameStart = ZIPPY_GAME_START;\r
1914   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1915   appData.zippyAbort = ZIPPY_ABORT;\r
1916   appData.zippyVariants = ZIPPY_VARIANTS;\r
1917   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1918   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1919 #endif\r
1920 \r
1921   /* Point font array elements to structures and\r
1922      parse default font names */\r
1923   for (i=0; i<NUM_FONTS; i++) {\r
1924     for (j=0; j<NUM_SIZES; j++) {\r
1925       font[j][i] = &fontRec[j][i];\r
1926       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1927     }\r
1928   }\r
1929   \r
1930   /* Parse default settings file if any */\r
1931   if (ParseSettingsFile(settingsFileName, buf)) {\r
1932     settingsFileName = strdup(buf);\r
1933   }\r
1934 \r
1935   /* Parse command line */\r
1936   ParseArgs(StringGet, &lpCmdLine);\r
1937 \r
1938   /* [HGM] make sure board size is acceptable */\r
1939   if(appData.NrFiles > BOARD_SIZE ||\r
1940      appData.NrRanks > BOARD_SIZE   )\r
1941       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1942 \r
1943   /* Propagate options that affect others */\r
1944   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1945   if (appData.icsActive || appData.noChessProgram) {\r
1946      chessProgram = FALSE;  /* not local chess program mode */\r
1947   }\r
1948 \r
1949   /* Open startup dialog if needed */\r
1950   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1951       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1952       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1953                         *appData.secondChessProgram == NULLCHAR))) {\r
1954     FARPROC lpProc;\r
1955     \r
1956     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1957     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1958     FreeProcInstance(lpProc);\r
1959   }\r
1960 \r
1961   /* Make sure save files land in the right (?) directory */\r
1962   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1963     appData.saveGameFile = strdup(buf);\r
1964   }\r
1965   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1966     appData.savePositionFile = strdup(buf);\r
1967   }\r
1968 \r
1969   /* Finish initialization for fonts and sounds */\r
1970   for (i=0; i<NUM_FONTS; i++) {\r
1971     for (j=0; j<NUM_SIZES; j++) {\r
1972       CreateFontInMF(font[j][i]);\r
1973     }\r
1974   }\r
1975   /* xboard, and older WinBoards, controlled the move sound with the\r
1976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1977      always turn the option on (so that the backend will call us),\r
1978      then let the user turn the sound off by setting it to silence if\r
1979      desired.  To accommodate old winboard.ini files saved by old\r
1980      versions of WinBoard, we also turn off the sound if the option\r
1981      was initially set to false. */\r
1982   if (!appData.ringBellAfterMoves) {\r
1983     sounds[(int)SoundMove].name = strdup("");\r
1984     appData.ringBellAfterMoves = TRUE;\r
1985   }\r
1986   GetCurrentDirectory(MSG_SIZ, currDir);\r
1987   SetCurrentDirectory(installDir);\r
1988   LoadAllSounds();\r
1989   SetCurrentDirectory(currDir);\r
1990 \r
1991   p = icsTextMenuString;\r
1992   if (p[0] == '@') {\r
1993     FILE* f = fopen(p + 1, "r");\r
1994     if (f == NULL) {\r
1995       DisplayFatalError(p + 1, errno, 2);\r
1996       return;\r
1997     }\r
1998     i = fread(buf, 1, sizeof(buf)-1, f);\r
1999     fclose(f);\r
2000     buf[i] = NULLCHAR;\r
2001     p = buf;\r
2002   }\r
2003   ParseIcsTextMenu(strdup(p));\r
2004 }\r
2005 \r
2006 \r
2007 VOID\r
2008 InitMenuChecks()\r
2009 {\r
2010   HMENU hmenu = GetMenu(hwndMain);\r
2011 \r
2012   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2013                         MF_BYCOMMAND|((appData.icsActive &&\r
2014                                        *appData.icsCommPort != NULLCHAR) ?\r
2015                                       MF_ENABLED : MF_GRAYED));\r
2016   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2017                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2018                                      MF_CHECKED : MF_UNCHECKED));\r
2019 }\r
2020 \r
2021 \r
2022 VOID\r
2023 SaveSettings(char* name)\r
2024 {\r
2025   FILE *f;\r
2026   ArgDescriptor *ad;\r
2027   WINDOWPLACEMENT wp;\r
2028   char dir[MSG_SIZ];\r
2029 \r
2030   if (!hwndMain) return;\r
2031 \r
2032   GetCurrentDirectory(MSG_SIZ, dir);\r
2033   SetCurrentDirectory(installDir);\r
2034   f = fopen(name, "w");\r
2035   SetCurrentDirectory(dir);\r
2036   if (f == NULL) {\r
2037     DisplayError(name, errno);\r
2038     return;\r
2039   }\r
2040   fprintf(f, ";\n");\r
2041   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2042   fprintf(f, ";\n");\r
2043   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2044   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2045   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2046   fprintf(f, ";\n");\r
2047 \r
2048   wp.length = sizeof(WINDOWPLACEMENT);\r
2049   GetWindowPlacement(hwndMain, &wp);\r
2050   boardX = wp.rcNormalPosition.left;\r
2051   boardY = wp.rcNormalPosition.top;\r
2052 \r
2053   if (hwndConsole) {\r
2054     GetWindowPlacement(hwndConsole, &wp);\r
2055     consoleX = wp.rcNormalPosition.left;\r
2056     consoleY = wp.rcNormalPosition.top;\r
2057     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2058     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2059   }\r
2060 \r
2061   if (analysisDialog) {\r
2062     GetWindowPlacement(analysisDialog, &wp);\r
2063     analysisX = wp.rcNormalPosition.left;\r
2064     analysisY = wp.rcNormalPosition.top;\r
2065     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2066     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2067   }\r
2068 \r
2069   if (commentDialog) {\r
2070     GetWindowPlacement(commentDialog, &wp);\r
2071     commentX = wp.rcNormalPosition.left;\r
2072     commentY = wp.rcNormalPosition.top;\r
2073     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2074     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2075   }\r
2076 \r
2077   if (editTagsDialog) {\r
2078     GetWindowPlacement(editTagsDialog, &wp);\r
2079     editTagsX = wp.rcNormalPosition.left;\r
2080     editTagsY = wp.rcNormalPosition.top;\r
2081     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2082     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2083   }\r
2084 \r
2085   if (gameListDialog) {\r
2086     GetWindowPlacement(gameListDialog, &wp);\r
2087     gameListX = wp.rcNormalPosition.left;\r
2088     gameListY = wp.rcNormalPosition.top;\r
2089     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2090     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2091   }\r
2092 \r
2093   /* [AS] Move history */\r
2094   wpMoveHistory.visible = MoveHistoryIsUp();\r
2095   \r
2096   if( moveHistoryDialog ) {\r
2097     GetWindowPlacement(moveHistoryDialog, &wp);\r
2098     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2099     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2100     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2101     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2102   }\r
2103 \r
2104   /* [AS] Eval graph */\r
2105   wpEvalGraph.visible = EvalGraphIsUp();\r
2106 \r
2107   if( evalGraphDialog ) {\r
2108     GetWindowPlacement(evalGraphDialog, &wp);\r
2109     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2110     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2111     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2112     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2113   }\r
2114 \r
2115   /* [AS] Engine output */\r
2116   wpEngineOutput.visible = EngineOutputIsUp();\r
2117 \r
2118   if( engineOutputDialog ) {\r
2119     GetWindowPlacement(engineOutputDialog, &wp);\r
2120     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2121     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2122     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2123     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2124   }\r
2125 \r
2126   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2127     if (!ad->save) continue;\r
2128     switch (ad->argType) {\r
2129     case ArgString:\r
2130       {\r
2131         char *p = *(char **)ad->argLoc;\r
2132         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2133           /* Quote multiline values or \-containing values\r
2134              with { } if possible */\r
2135           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2136         } else {\r
2137           /* Else quote with " " */\r
2138           fprintf(f, "/%s=\"", ad->argName);\r
2139           while (*p) {\r
2140             if (*p == '\n') fprintf(f, "\n");\r
2141             else if (*p == '\r') fprintf(f, "\\r");\r
2142             else if (*p == '\t') fprintf(f, "\\t");\r
2143             else if (*p == '\b') fprintf(f, "\\b");\r
2144             else if (*p == '\f') fprintf(f, "\\f");\r
2145             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2146             else if (*p == '\"') fprintf(f, "\\\"");\r
2147             else if (*p == '\\') fprintf(f, "\\\\");\r
2148             else putc(*p, f);\r
2149             p++;\r
2150           }\r
2151           fprintf(f, "\"\n");\r
2152         }\r
2153       }\r
2154       break;\r
2155     case ArgInt:\r
2156       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2157       break;\r
2158     case ArgFloat:\r
2159       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2160       break;\r
2161     case ArgBoolean:\r
2162       fprintf(f, "/%s=%s\n", ad->argName, \r
2163         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2164       break;\r
2165     case ArgTrue:\r
2166       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2167       break;\r
2168     case ArgFalse:\r
2169       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2170       break;\r
2171     case ArgColor:\r
2172       {\r
2173         COLORREF color = *(COLORREF *)ad->argLoc;\r
2174         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2175           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2176       }\r
2177       break;\r
2178     case ArgAttribs:\r
2179       {\r
2180         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2181         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2182           (ta->effects & CFE_BOLD) ? "b" : "",\r
2183           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2184           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2185           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2186           (ta->effects) ? " " : "",\r
2187           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2188       }\r
2189       break;\r
2190     case ArgFilename:\r
2191       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2192         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2193       } else {\r
2194         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2195       }\r
2196       break;\r
2197     case ArgBoardSize:\r
2198       fprintf(f, "/%s=%s\n", ad->argName,\r
2199               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2200       break;\r
2201     case ArgFont:\r
2202       {\r
2203         int bs;\r
2204         for (bs=0; bs<NUM_SIZES; bs++) {\r
2205           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2206           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2207           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2208             ad->argName, mfp->faceName, mfp->pointSize,\r
2209             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2210             mfp->bold ? "b" : "",\r
2211             mfp->italic ? "i" : "",\r
2212             mfp->underline ? "u" : "",\r
2213             mfp->strikeout ? "s" : "");\r
2214         }\r
2215       }\r
2216       break;\r
2217     case ArgCommSettings:\r
2218       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2219     }\r
2220   }\r
2221   fclose(f);\r
2222 }\r
2223 \r
2224 \r
2225 \r
2226 /*---------------------------------------------------------------------------*\\r
2227  *\r
2228  * GDI board drawing routines\r
2229  *\r
2230 \*---------------------------------------------------------------------------*/\r
2231 \r
2232 /* [AS] Draw square using background texture */\r
2233 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2234 {\r
2235     XFORM   x;\r
2236 \r
2237     if( mode == 0 ) {\r
2238         return; /* Should never happen! */\r
2239     }\r
2240 \r
2241     SetGraphicsMode( dst, GM_ADVANCED );\r
2242 \r
2243     switch( mode ) {\r
2244     case 1:\r
2245         /* Identity */\r
2246         break;\r
2247     case 2:\r
2248         /* X reflection */\r
2249         x.eM11 = -1.0;\r
2250         x.eM12 = 0;\r
2251         x.eM21 = 0;\r
2252         x.eM22 = 1.0;\r
2253         x.eDx = (FLOAT) dw + dx - 1;\r
2254         x.eDy = 0;\r
2255         dx = 0;\r
2256         SetWorldTransform( dst, &x );\r
2257         break;\r
2258     case 3:\r
2259         /* Y reflection */\r
2260         x.eM11 = 1.0;\r
2261         x.eM12 = 0;\r
2262         x.eM21 = 0;\r
2263         x.eM22 = -1.0;\r
2264         x.eDx = 0;\r
2265         x.eDy = (FLOAT) dh + dy - 1;\r
2266         dy = 0;\r
2267         SetWorldTransform( dst, &x );\r
2268         break;\r
2269     case 4:\r
2270         /* X/Y flip */\r
2271         x.eM11 = 0;\r
2272         x.eM12 = 1.0;\r
2273         x.eM21 = 1.0;\r
2274         x.eM22 = 0;\r
2275         x.eDx = (FLOAT) dx;\r
2276         x.eDy = (FLOAT) dy;\r
2277         dx = 0;\r
2278         dy = 0;\r
2279         SetWorldTransform( dst, &x );\r
2280         break;\r
2281     }\r
2282 \r
2283     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2284 \r
2285     x.eM11 = 1.0;\r
2286     x.eM12 = 0;\r
2287     x.eM21 = 0;\r
2288     x.eM22 = 1.0;\r
2289     x.eDx = 0;\r
2290     x.eDy = 0;\r
2291     SetWorldTransform( dst, &x );\r
2292 \r
2293     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2294 }\r
2295 \r
2296 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2297 enum {\r
2298     PM_WP = (int) WhitePawn, \r
2299     PM_WN = (int) WhiteKnight, \r
2300     PM_WB = (int) WhiteBishop, \r
2301     PM_WR = (int) WhiteRook, \r
2302     PM_WQ = (int) WhiteQueen, \r
2303     PM_WF = (int) WhiteFerz, \r
2304     PM_WW = (int) WhiteWazir, \r
2305     PM_WE = (int) WhiteAlfil, \r
2306     PM_WM = (int) WhiteMan, \r
2307     PM_WO = (int) WhiteCannon, \r
2308     PM_WU = (int) WhiteUnicorn, \r
2309     PM_WH = (int) WhiteNightrider, \r
2310     PM_WA = (int) WhiteCardinal, \r
2311     PM_WC = (int) WhiteMarshall, \r
2312     PM_WG = (int) WhiteGrasshopper, \r
2313     PM_WK = (int) WhiteKing,\r
2314     PM_BP = (int) BlackPawn, \r
2315     PM_BN = (int) BlackKnight, \r
2316     PM_BB = (int) BlackBishop, \r
2317     PM_BR = (int) BlackRook, \r
2318     PM_BQ = (int) BlackQueen, \r
2319     PM_BF = (int) BlackFerz, \r
2320     PM_BW = (int) BlackWazir, \r
2321     PM_BE = (int) BlackAlfil, \r
2322     PM_BM = (int) BlackMan,\r
2323     PM_BO = (int) BlackCannon, \r
2324     PM_BU = (int) BlackUnicorn, \r
2325     PM_BH = (int) BlackNightrider, \r
2326     PM_BA = (int) BlackCardinal, \r
2327     PM_BC = (int) BlackMarshall, \r
2328     PM_BG = (int) BlackGrasshopper, \r
2329     PM_BK = (int) BlackKing\r
2330 };\r
2331 \r
2332 static HFONT hPieceFont = NULL;\r
2333 static HBITMAP hPieceMask[(int) EmptySquare];\r
2334 static HBITMAP hPieceFace[(int) EmptySquare];\r
2335 static int fontBitmapSquareSize = 0;\r
2336 static char pieceToFontChar[(int) EmptySquare] =\r
2337                               { 'p', 'n', 'b', 'r', 'q', \r
2338                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2339                       'k', 'o', 'm', 'v', 't', 'w', \r
2340                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2341                                                               'l' };\r
2342 \r
2343 extern BOOL SetCharTable( char *table, const char * map );\r
2344 /* [HGM] moved to backend.c */\r
2345 \r
2346 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2347 {\r
2348     HBRUSH hbrush;\r
2349     BYTE r1 = GetRValue( color );\r
2350     BYTE g1 = GetGValue( color );\r
2351     BYTE b1 = GetBValue( color );\r
2352     BYTE r2 = r1 / 2;\r
2353     BYTE g2 = g1 / 2;\r
2354     BYTE b2 = b1 / 2;\r
2355     RECT rc;\r
2356 \r
2357     /* Create a uniform background first */\r
2358     hbrush = CreateSolidBrush( color );\r
2359     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2360     FillRect( hdc, &rc, hbrush );\r
2361     DeleteObject( hbrush );\r
2362     \r
2363     if( mode == 1 ) {\r
2364         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2365         int steps = squareSize / 2;\r
2366         int i;\r
2367 \r
2368         for( i=0; i<steps; i++ ) {\r
2369             BYTE r = r1 - (r1-r2) * i / steps;\r
2370             BYTE g = g1 - (g1-g2) * i / steps;\r
2371             BYTE b = b1 - (b1-b2) * i / steps;\r
2372 \r
2373             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2374             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2375             FillRect( hdc, &rc, hbrush );\r
2376             DeleteObject(hbrush);\r
2377         }\r
2378     }\r
2379     else if( mode == 2 ) {\r
2380         /* Diagonal gradient, good more or less for every piece */\r
2381         POINT triangle[3];\r
2382         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2383         HBRUSH hbrush_old;\r
2384         int steps = squareSize;\r
2385         int i;\r
2386 \r
2387         triangle[0].x = squareSize - steps;\r
2388         triangle[0].y = squareSize;\r
2389         triangle[1].x = squareSize;\r
2390         triangle[1].y = squareSize;\r
2391         triangle[2].x = squareSize;\r
2392         triangle[2].y = squareSize - steps;\r
2393 \r
2394         for( i=0; i<steps; i++ ) {\r
2395             BYTE r = r1 - (r1-r2) * i / steps;\r
2396             BYTE g = g1 - (g1-g2) * i / steps;\r
2397             BYTE b = b1 - (b1-b2) * i / steps;\r
2398 \r
2399             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2400             hbrush_old = SelectObject( hdc, hbrush );\r
2401             Polygon( hdc, triangle, 3 );\r
2402             SelectObject( hdc, hbrush_old );\r
2403             DeleteObject(hbrush);\r
2404             triangle[0].x++;\r
2405             triangle[2].y++;\r
2406         }\r
2407 \r
2408         SelectObject( hdc, hpen );\r
2409     }\r
2410 }\r
2411 \r
2412 /*\r
2413     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2414     seems to work ok. The main problem here is to find the "inside" of a chess\r
2415     piece: follow the steps as explained below.\r
2416 */\r
2417 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2418 {\r
2419     HBITMAP hbm;\r
2420     HBITMAP hbm_old;\r
2421     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2422     RECT rc;\r
2423     SIZE sz;\r
2424     POINT pt;\r
2425     int backColor = whitePieceColor; \r
2426     int foreColor = blackPieceColor;\r
2427     int shapeIndex = index < 6 ? index+6 : index;\r
2428     \r
2429     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2430         backColor = appData.fontBackColorWhite;\r
2431         foreColor = appData.fontForeColorWhite;\r
2432     }\r
2433     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2434         backColor = appData.fontBackColorBlack;\r
2435         foreColor = appData.fontForeColorBlack;\r
2436     }\r
2437 \r
2438     /* Mask */\r
2439     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2440 \r
2441     hbm_old = SelectObject( hdc, hbm );\r
2442 \r
2443     rc.left = 0;\r
2444     rc.top = 0;\r
2445     rc.right = squareSize;\r
2446     rc.bottom = squareSize;\r
2447 \r
2448     /* Step 1: background is now black */\r
2449     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2450 \r
2451     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2452 \r
2453     pt.x = (squareSize - sz.cx) / 2;\r
2454     pt.y = (squareSize - sz.cy) / 2;\r
2455 \r
2456     SetBkMode( hdc, TRANSPARENT );\r
2457     SetTextColor( hdc, chroma );\r
2458     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2459     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2460 \r
2461     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2462     /* Step 3: the area outside the piece is filled with white */\r
2463     FloodFill( hdc, 0, 0, chroma );\r
2464     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2465     /* \r
2466         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2467         but if the start point is not inside the piece we're lost!\r
2468         There should be a better way to do this... if we could create a region or path\r
2469         from the fill operation we would be fine for example.\r
2470     */\r
2471     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2472 \r
2473     SetTextColor( hdc, 0 );\r
2474     /* \r
2475         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2476         draw the piece again in black for safety.\r
2477     */\r
2478     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2479 \r
2480     SelectObject( hdc, hbm_old );\r
2481 \r
2482     if( hPieceMask[index] != NULL ) {\r
2483         DeleteObject( hPieceMask[index] );\r
2484     }\r
2485 \r
2486     hPieceMask[index] = hbm;\r
2487 \r
2488     /* Face */\r
2489     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2490 \r
2491     SelectObject( hdc, hbm );\r
2492 \r
2493     {\r
2494         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2495         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2496         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2497 \r
2498         SelectObject( dc1, hPieceMask[index] );\r
2499         SelectObject( dc2, bm2 );\r
2500         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2501         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2502         \r
2503         /* \r
2504             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2505             the piece background and deletes (makes transparent) the rest.\r
2506             Thanks to that mask, we are free to paint the background with the greates\r
2507             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2508             We use this, to make gradients and give the pieces a "roundish" look.\r
2509         */\r
2510         SetPieceBackground( hdc, backColor, 2 );\r
2511         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2512 \r
2513         DeleteDC( dc2 );\r
2514         DeleteDC( dc1 );\r
2515         DeleteObject( bm2 );\r
2516     }\r
2517 \r
2518     SetTextColor( hdc, foreColor );\r
2519     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2520 \r
2521     SelectObject( hdc, hbm_old );\r
2522 \r
2523     if( hPieceFace[index] != NULL ) {\r
2524         DeleteObject( hPieceFace[index] );\r
2525     }\r
2526 \r
2527     hPieceFace[index] = hbm;\r
2528 }\r
2529 \r
2530 static int TranslatePieceToFontPiece( int piece )\r
2531 {\r
2532     switch( piece ) {\r
2533     case BlackPawn:\r
2534         return PM_BP;\r
2535     case BlackKnight:\r
2536         return PM_BN;\r
2537     case BlackBishop:\r
2538         return PM_BB;\r
2539     case BlackRook:\r
2540         return PM_BR;\r
2541     case BlackQueen:\r
2542         return PM_BQ;\r
2543     case BlackKing:\r
2544         return PM_BK;\r
2545     case WhitePawn:\r
2546         return PM_WP;\r
2547     case WhiteKnight:\r
2548         return PM_WN;\r
2549     case WhiteBishop:\r
2550         return PM_WB;\r
2551     case WhiteRook:\r
2552         return PM_WR;\r
2553     case WhiteQueen:\r
2554         return PM_WQ;\r
2555     case WhiteKing:\r
2556         return PM_WK;\r
2557 #ifdef FAIRY\r
2558     case BlackCardinal:\r
2559         return PM_BA;\r
2560     case BlackMarshall:\r
2561         return PM_BC;\r
2562     case BlackFerz:\r
2563         return PM_BF;\r
2564     case BlackNightrider:\r
2565         return PM_BH;\r
2566     case BlackAlfil:\r
2567         return PM_BE;\r
2568     case BlackWazir:\r
2569         return PM_BW;\r
2570     case BlackUnicorn:\r
2571         return PM_BU;\r
2572     case BlackCannon:\r
2573         return PM_BO;\r
2574     case BlackGrasshopper:\r
2575         return PM_BG;\r
2576     case BlackMan:\r
2577         return PM_BM;\r
2578     case WhiteCardinal:\r
2579         return PM_WA;\r
2580     case WhiteMarshall:\r
2581         return PM_WC;\r
2582     case WhiteFerz:\r
2583         return PM_WF;\r
2584     case WhiteNightrider:\r
2585         return PM_WH;\r
2586     case WhiteAlfil:\r
2587         return PM_WE;\r
2588     case WhiteWazir:\r
2589         return PM_WW;\r
2590     case WhiteUnicorn:\r
2591         return PM_WU;\r
2592     case WhiteCannon:\r
2593         return PM_WO;\r
2594     case WhiteGrasshopper:\r
2595         return PM_WG;\r
2596     case WhiteMan:\r
2597         return PM_WM;\r
2598 #endif\r
2599     }\r
2600 \r
2601     return 0;\r
2602 }\r
2603 \r
2604 void CreatePiecesFromFont()\r
2605 {\r
2606     LOGFONT lf;\r
2607     HDC hdc_window = NULL;\r
2608     HDC hdc = NULL;\r
2609     HFONT hfont_old;\r
2610     int fontHeight;\r
2611     int i;\r
2612 \r
2613     if( fontBitmapSquareSize < 0 ) {\r
2614         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2615         return;\r
2616     }\r
2617 \r
2618     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2619         fontBitmapSquareSize = -1;\r
2620         return;\r
2621     }\r
2622 \r
2623     if( fontBitmapSquareSize != squareSize ) {\r
2624         hdc_window = GetDC( hwndMain );\r
2625         hdc = CreateCompatibleDC( hdc_window );\r
2626 \r
2627         if( hPieceFont != NULL ) {\r
2628             DeleteObject( hPieceFont );\r
2629         }\r
2630         else {\r
2631             for( i=0; i<12; i++ ) {\r
2632                 hPieceMask[i] = NULL;\r
2633                 hPieceFace[i] = NULL;\r
2634             }\r
2635         }\r
2636 \r
2637         fontHeight = 75;\r
2638 \r
2639         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2640             fontHeight = appData.fontPieceSize;\r
2641         }\r
2642 \r
2643         fontHeight = (fontHeight * squareSize) / 100;\r
2644 \r
2645         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2646         lf.lfWidth = 0;\r
2647         lf.lfEscapement = 0;\r
2648         lf.lfOrientation = 0;\r
2649         lf.lfWeight = FW_NORMAL;\r
2650         lf.lfItalic = 0;\r
2651         lf.lfUnderline = 0;\r
2652         lf.lfStrikeOut = 0;\r
2653         lf.lfCharSet = DEFAULT_CHARSET;\r
2654         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2655         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2656         lf.lfQuality = PROOF_QUALITY;\r
2657         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2658         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2659         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2660 \r
2661         hPieceFont = CreateFontIndirect( &lf );\r
2662 \r
2663         if( hPieceFont == NULL ) {\r
2664             fontBitmapSquareSize = -2;\r
2665         }\r
2666         else {\r
2667             /* Setup font-to-piece character table */\r
2668             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2669                 /* No (or wrong) global settings, try to detect the font */\r
2670                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2671                     /* Alpha */\r
2672                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2673                 }\r
2674                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2675                     /* DiagramTT* family */\r
2676                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2677                 }\r
2678                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2679                     /* Fairy symbols */\r
2680                      SetCharTable(pieceToFontChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk");\r
2681                 }\r
2682                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2683                     /* Good Companion (Some characters get warped as literal :-( */\r
2684                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2685                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2686                     SetCharTable(pieceToFontChar, s);\r
2687                 }\r
2688                 else {\r
2689                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2690                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2691                 }\r
2692             }\r
2693 \r
2694             /* Create bitmaps */\r
2695             hfont_old = SelectObject( hdc, hPieceFont );\r
2696 \r
2697             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2698             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2699             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2700             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2701             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2702             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2703             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2704             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2705             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2706             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2707             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2708             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2709 #ifdef FAIRY\r
2710             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2711             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2712             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2713             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2714             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2715             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2716             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2717             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2718             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2719             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2720             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2721             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2722             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2723             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2724             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2725             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2726             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2727             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2728             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2729             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2730 #endif\r
2731 \r
2732             SelectObject( hdc, hfont_old );\r
2733 \r
2734             fontBitmapSquareSize = squareSize;\r
2735         }\r
2736     }\r
2737 \r
2738     if( hdc != NULL ) {\r
2739         DeleteDC( hdc );\r
2740     }\r
2741 \r
2742     if( hdc_window != NULL ) {\r
2743         ReleaseDC( hwndMain, hdc_window );\r
2744     }\r
2745 }\r
2746 \r
2747 HBITMAP\r
2748 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2749 {\r
2750   char name[128];\r
2751 \r
2752   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2753   if (gameInfo.event &&\r
2754       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2755       strcmp(name, "k80s") == 0) {\r
2756     strcpy(name, "tim");\r
2757   }\r
2758   return LoadBitmap(hinst, name);\r
2759 }\r
2760 \r
2761 \r
2762 /* Insert a color into the program's logical palette\r
2763    structure.  This code assumes the given color is\r
2764    the result of the RGB or PALETTERGB macro, and it\r
2765    knows how those macros work (which is documented).\r
2766 */\r
2767 VOID\r
2768 InsertInPalette(COLORREF color)\r
2769 {\r
2770   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2771 \r
2772   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2773     DisplayFatalError("Too many colors", 0, 1);\r
2774     pLogPal->palNumEntries--;\r
2775     return;\r
2776   }\r
2777 \r
2778   pe->peFlags = (char) 0;\r
2779   pe->peRed = (char) (0xFF & color);\r
2780   pe->peGreen = (char) (0xFF & (color >> 8));\r
2781   pe->peBlue = (char) (0xFF & (color >> 16));\r
2782   return;\r
2783 }\r
2784 \r
2785 \r
2786 VOID\r
2787 InitDrawingColors()\r
2788 {\r
2789   if (pLogPal == NULL) {\r
2790     /* Allocate enough memory for a logical palette with\r
2791      * PALETTESIZE entries and set the size and version fields\r
2792      * of the logical palette structure.\r
2793      */\r
2794     pLogPal = (NPLOGPALETTE)\r
2795       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2796                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2797     pLogPal->palVersion    = 0x300;\r
2798   }\r
2799   pLogPal->palNumEntries = 0;\r
2800 \r
2801   InsertInPalette(lightSquareColor);\r
2802   InsertInPalette(darkSquareColor);\r
2803   InsertInPalette(whitePieceColor);\r
2804   InsertInPalette(blackPieceColor);\r
2805   InsertInPalette(highlightSquareColor);\r
2806   InsertInPalette(premoveHighlightColor);\r
2807 \r
2808   /*  create a logical color palette according the information\r
2809    *  in the LOGPALETTE structure.\r
2810    */\r
2811   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2812 \r
2813   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2814   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2815   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2816   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2817   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2818   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2819 \r
2820   /* [AS] Force rendering of the font-based pieces */\r
2821   if( fontBitmapSquareSize > 0 ) {\r
2822     fontBitmapSquareSize = 0;\r
2823   }\r
2824 }\r
2825 \r
2826 \r
2827 int\r
2828 BoardWidth(int boardSize, int n)\r
2829 { /* [HGM] argument n added to allow different width and height */\r
2830   int lineGap = sizeInfo[boardSize].lineGap;\r
2831 \r
2832   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2833       lineGap = appData.overrideLineGap;\r
2834   }\r
2835 \r
2836   return (n + 1) * lineGap +\r
2837           n * sizeInfo[boardSize].squareSize;\r
2838 }\r
2839 \r
2840 /* Respond to board resize by dragging edge */\r
2841 VOID\r
2842 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2843 {\r
2844   BoardSize newSize = NUM_SIZES - 1;\r
2845   static int recurse = 0;\r
2846   if (IsIconic(hwndMain)) return;\r
2847   if (recurse > 0) return;\r
2848   recurse++;\r
2849   while (newSize > 0 &&\r
2850          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2851           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2852     newSize--;\r
2853   } \r
2854   boardSize = newSize;\r
2855   InitDrawingSizes(boardSize, flags);\r
2856   recurse--;\r
2857 }\r
2858 \r
2859 \r
2860 \r
2861 VOID\r
2862 InitDrawingSizes(BoardSize boardSize, int flags)\r
2863 {\r
2864   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2865   ChessSquare piece;\r
2866   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2867   HDC hdc;\r
2868   SIZE clockSize, messageSize;\r
2869   HFONT oldFont;\r
2870   char buf[MSG_SIZ];\r
2871   char *str;\r
2872   HMENU hmenu = GetMenu(hwndMain);\r
2873   RECT crect, wrect;\r
2874   int offby;\r
2875   LOGBRUSH logbrush;\r
2876 \r
2877   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
2878   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2879 \r
2880   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2881   smallLayout = sizeInfo[boardSize].smallLayout;\r
2882   squareSize = sizeInfo[boardSize].squareSize;\r
2883   lineGap = sizeInfo[boardSize].lineGap;\r
2884   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2885 \r
2886   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2887       lineGap = appData.overrideLineGap;\r
2888   }\r
2889 \r
2890   if (tinyLayout != oldTinyLayout) {\r
2891     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2892     if (tinyLayout) {\r
2893       style &= ~WS_SYSMENU;\r
2894       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2895                  "&Minimize\tCtrl+F4");\r
2896     } else {\r
2897       style |= WS_SYSMENU;\r
2898       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2899     }\r
2900     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2901 \r
2902     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2903       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2904         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2905     }\r
2906     DrawMenuBar(hwndMain);\r
2907   }\r
2908 \r
2909   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2910   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2911 \r
2912   /* Get text area sizes */\r
2913   hdc = GetDC(hwndMain);\r
2914   if (appData.clockMode) {\r
2915     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2916   } else {\r
2917     sprintf(buf, "White");\r
2918   }\r
2919   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2920   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2921   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2922   str = "We only care about the height here";\r
2923   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2924   SelectObject(hdc, oldFont);\r
2925   ReleaseDC(hwndMain, hdc);\r
2926 \r
2927   /* Compute where everything goes */\r
2928   whiteRect.left = OUTER_MARGIN;\r
2929   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2930   whiteRect.top = OUTER_MARGIN;\r
2931   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2932 \r
2933   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2934   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2935   blackRect.top = whiteRect.top;\r
2936   blackRect.bottom = whiteRect.bottom;\r
2937 \r
2938   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2939   if (appData.showButtonBar) {\r
2940     messageRect.right = blackRect.right\r
2941       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2942   } else {\r
2943     messageRect.right = blackRect.right;\r
2944   }\r
2945   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2946   messageRect.bottom = messageRect.top + messageSize.cy;\r
2947 \r
2948   boardRect.left = whiteRect.left;\r
2949   boardRect.right = boardRect.left + boardWidth;\r
2950   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2951   boardRect.bottom = boardRect.top + boardHeight;\r
2952 \r
2953   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2954   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2955   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2956   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2957     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2958   GetWindowRect(hwndMain, &wrect);\r
2959   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2960                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2961   /* compensate if menu bar wrapped */\r
2962   GetClientRect(hwndMain, &crect);\r
2963   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2964   winHeight += offby;\r
2965   switch (flags) {\r
2966   case WMSZ_TOPLEFT:\r
2967     SetWindowPos(hwndMain, NULL, \r
2968                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2969                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2970     break;\r
2971 \r
2972   case WMSZ_TOPRIGHT:\r
2973   case WMSZ_TOP:\r
2974     SetWindowPos(hwndMain, NULL, \r
2975                  wrect.left, wrect.bottom - winHeight, \r
2976                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2977     break;\r
2978 \r
2979   case WMSZ_BOTTOMLEFT:\r
2980   case WMSZ_LEFT:\r
2981     SetWindowPos(hwndMain, NULL, \r
2982                  wrect.right - winWidth, wrect.top, \r
2983                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2984     break;\r
2985 \r
2986   case WMSZ_BOTTOMRIGHT:\r
2987   case WMSZ_BOTTOM:\r
2988   case WMSZ_RIGHT:\r
2989   default:\r
2990     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2991                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2992     break;\r
2993   }\r
2994 \r
2995   hwndPause = NULL;\r
2996   for (i = 0; i < N_BUTTONS; i++) {\r
2997     if (buttonDesc[i].hwnd != NULL) {\r
2998       DestroyWindow(buttonDesc[i].hwnd);\r
2999       buttonDesc[i].hwnd = NULL;\r
3000     }\r
3001     if (appData.showButtonBar) {\r
3002       buttonDesc[i].hwnd =\r
3003         CreateWindow("BUTTON", buttonDesc[i].label,\r
3004                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3005                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3006                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3007                      (HMENU) buttonDesc[i].id,\r
3008                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3009       if (tinyLayout) {\r
3010         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3011                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3012                     MAKELPARAM(FALSE, 0));\r
3013       }\r
3014       if (buttonDesc[i].id == IDM_Pause)\r
3015         hwndPause = buttonDesc[i].hwnd;\r
3016       buttonDesc[i].wndproc = (WNDPROC)\r
3017         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3018     }\r
3019   }\r
3020   if (gridPen != NULL) DeleteObject(gridPen);\r
3021   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3022   if (premovePen != NULL) DeleteObject(premovePen);\r
3023   if (lineGap != 0) {\r
3024     logbrush.lbStyle = BS_SOLID;\r
3025     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3026     gridPen =\r
3027       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3028                    lineGap, &logbrush, 0, NULL);\r
3029     logbrush.lbColor = highlightSquareColor;\r
3030     highlightPen =\r
3031       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3032                    lineGap, &logbrush, 0, NULL);\r
3033 \r
3034     logbrush.lbColor = premoveHighlightColor; \r
3035     premovePen =\r
3036       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3037                    lineGap, &logbrush, 0, NULL);\r
3038 \r
3039     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3040     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3041       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3042       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3043         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3044       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3045         BOARD_WIDTH * (squareSize + lineGap);\r
3046         lineGap / 2 + (i * (squareSize + lineGap));\r
3047       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3048     }\r
3049     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3050       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3051       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3052         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3053         lineGap / 2 + (i * (squareSize + lineGap));\r
3054       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3055         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3056       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3057     }\r
3058   }\r
3059 \r
3060 #ifdef GOTHIC\r
3061   /* [HGM] Gothic licensing requirement */\r
3062   GothicPopUp( GOTHIC, gameInfo.variant == VariantGothic );\r
3063 #endif\r
3064 \r
3065 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3066   oldBoardSize = boardSize;\r
3067   oldTinyLayout = tinyLayout;\r
3068 \r
3069   /* Load piece bitmaps for this board size */\r
3070   for (i=0; i<=2; i++) {\r
3071     for (piece = WhitePawn;\r
3072          (int) piece < (int) BlackPawn;\r
3073          piece = (ChessSquare) ((int) piece + 1)) {\r
3074       if (pieceBitmap[i][piece] != NULL)\r
3075         DeleteObject(pieceBitmap[i][piece]);\r
3076     }\r
3077   }\r
3078 \r
3079   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3080   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3081   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3082   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3083   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3084   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3085   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3086   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3087   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3088   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3089   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3090   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3091   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3092   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3093   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3094   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3095   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3096   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3097   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3098   } else {\r
3099   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3100   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3101   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3102   }\r
3103   if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */\r
3104   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3105   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3106   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3107   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3108   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3109   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3110   pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3111   pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3112   pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3113   pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3114   pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3115   pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3116   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3117   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3118   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3119   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3120   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3121   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3122   if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3123   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3124   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3125   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3126   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3127   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3128   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3129   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3130   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3131   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3132   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3133   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3134   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3135   } else {\r
3136   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3137   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3138   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3139   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3140   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3141   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3142   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3143   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3144   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3145   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3146   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3147   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3148   }\r
3149   if(gameInfo.variant != VariantCrazyhouse && gameInfo.variant != VariantShogi) {\r
3150   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3151   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3152   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3153   } else {\r
3154   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3155   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3156   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3157   }\r
3158   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3159       if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3160       else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3161   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3162   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3163   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3164   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3165   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3166   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3167   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3168   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3169   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3170   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3171   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3172   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3173   }\r
3174 }\r
3175 \r
3176 HBITMAP\r
3177 PieceBitmap(ChessSquare p, int kind)\r
3178 {\r
3179   if ((int) p >= (int) BlackPawn)\r
3180     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3181 \r
3182   return pieceBitmap[kind][(int) p];\r
3183 }\r
3184 \r
3185 /***************************************************************/\r
3186 \r
3187 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3188 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3189 /*\r
3190 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3191 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3192 */\r
3193 \r
3194 VOID\r
3195 SquareToPos(int row, int column, int * x, int * y)\r
3196 {\r
3197   if (flipView) {\r
3198     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3199     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3200   } else {\r
3201     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3202     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3203   }\r
3204 }\r
3205 \r
3206 VOID\r
3207 DrawCoordsOnDC(HDC hdc)\r
3208 {\r
3209   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
3210   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
3211   char str[2] = { NULLCHAR, NULLCHAR };\r
3212   int oldMode, oldAlign, x, y, start, i;\r
3213   HFONT oldFont;\r
3214   HBRUSH oldBrush;\r
3215 \r
3216   if (!appData.showCoords)\r
3217     return;\r
3218 \r
3219   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3220 \r
3221   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3222   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3223   oldAlign = GetTextAlign(hdc);\r
3224   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3225 \r
3226   y = boardRect.top + lineGap;\r
3227   x = boardRect.left + lineGap;\r
3228 \r
3229   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3230   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3231     str[0] = files[start + i];\r
3232     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3233     y += squareSize + lineGap;\r
3234   }\r
3235 \r
3236   start = flipView ? 12-BOARD_WIDTH : 12;\r
3237 \r
3238   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3239   for (i = 0; i < BOARD_WIDTH; i++) {\r
3240     str[0] = ranks[start + i];\r
3241     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3242     x += squareSize + lineGap;\r
3243   }    \r
3244 \r
3245   SelectObject(hdc, oldBrush);\r
3246   SetBkMode(hdc, oldMode);\r
3247   SetTextAlign(hdc, oldAlign);\r
3248   SelectObject(hdc, oldFont);\r
3249 }\r
3250 \r
3251 VOID\r
3252 DrawGridOnDC(HDC hdc)\r
3253 {\r
3254   HPEN oldPen;\r
3255  \r
3256   if (lineGap != 0) {\r
3257     oldPen = SelectObject(hdc, gridPen);\r
3258     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3259     SelectObject(hdc, oldPen);\r
3260   }\r
3261 }\r
3262 \r
3263 #define HIGHLIGHT_PEN 0\r
3264 #define PREMOVE_PEN   1\r
3265 \r
3266 VOID\r
3267 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3268 {\r
3269   int x1, y1;\r
3270   HPEN oldPen, hPen;\r
3271   if (lineGap == 0) return;\r
3272   if (flipView) {\r
3273     x1 = boardRect.left +\r
3274       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3275     y1 = boardRect.top +\r
3276       lineGap/2 + y * (squareSize + lineGap);\r
3277   } else {\r
3278     x1 = boardRect.left +\r
3279       lineGap/2 + x * (squareSize + lineGap);\r
3280     y1 = boardRect.top +\r
3281       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3282   }\r
3283   hPen = pen ? premovePen : highlightPen;\r
3284   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3285   MoveToEx(hdc, x1, y1, NULL);\r
3286   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3287   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3288   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3289   LineTo(hdc, x1, y1);\r
3290   SelectObject(hdc, oldPen);\r
3291 }\r
3292 \r
3293 VOID\r
3294 DrawHighlightsOnDC(HDC hdc)\r
3295 {\r
3296   int i;\r
3297   for (i=0; i<2; i++) {\r
3298     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3299       DrawHighlightOnDC(hdc, TRUE,\r
3300                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3301                         HIGHLIGHT_PEN);\r
3302   }\r
3303   for (i=0; i<2; i++) {\r
3304     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3305         premoveHighlightInfo.sq[i].y >= 0) {\r
3306         DrawHighlightOnDC(hdc, TRUE,\r
3307                           premoveHighlightInfo.sq[i].x, \r
3308                           premoveHighlightInfo.sq[i].y,\r
3309                           PREMOVE_PEN);\r
3310     }\r
3311   }\r
3312 }\r
3313 \r
3314 /* Note: sqcolor is used only in monoMode */\r
3315 /* Note that this code is largely duplicated in woptions.c,\r
3316    function DrawSampleSquare, so that needs to be updated too */\r
3317 VOID\r
3318 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3319 {\r
3320   HBITMAP oldBitmap;\r
3321   HBRUSH oldBrush;\r
3322 \r
3323   if (appData.blindfold) return;\r
3324 \r
3325   /* [AS] Use font-based pieces if needed */\r
3326   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3327     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3328     CreatePiecesFromFont();\r
3329 \r
3330     if( fontBitmapSquareSize == squareSize ) {\r
3331         int index = TranslatePieceToFontPiece( piece );\r
3332 \r
3333         SelectObject( tmphdc, hPieceMask[ index ] );\r
3334 \r
3335         BitBlt( hdc,\r
3336             x, y,\r
3337             squareSize, squareSize,\r
3338             tmphdc,\r
3339             0, 0,\r
3340             SRCAND );\r
3341 \r
3342         SelectObject( tmphdc, hPieceFace[ index ] );\r
3343 \r
3344         BitBlt( hdc,\r
3345             x, y,\r
3346             squareSize, squareSize,\r
3347             tmphdc,\r
3348             0, 0,\r
3349             SRCPAINT );\r
3350 \r
3351         return;\r
3352     }\r
3353   }\r
3354 \r
3355   if (appData.monoMode) {\r
3356     SelectObject(tmphdc, PieceBitmap(piece, \r
3357       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3358     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3359            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3360   } else {\r
3361     if(minorSize &&\r
3362         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3363          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3364       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3365       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3366       x += (squareSize - minorSize)>>1;\r
3367       y += squareSize - minorSize - 2;\r
3368     }\r
3369     if (color || appData.allWhite ) {\r
3370       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3371       if( color )\r
3372               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3373       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3374       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3375 #if 0\r
3376       /* Use black piece color for outline of white pieces */\r
3377       /* Not sure this looks really good (though xboard does it).\r
3378          Maybe better to have another selectable color, default black */\r
3379       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3380       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3381       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3382 #else\r
3383       /* Use black for outline of white pieces */\r
3384       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3385       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
3386 #endif\r
3387     } else {\r
3388 #if 0\r
3389       /* Use white piece color for details of black pieces */\r
3390       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3391          WHITE_PIECE ones aren't always the right shape. */\r
3392       /* Not sure this looks really good (though xboard does it).\r
3393          Maybe better to have another selectable color, default medium gray? */\r
3394       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3395       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3396       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3397       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3398       SelectObject(hdc, blackPieceBrush);\r
3399       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3400 #else\r
3401       /* Use square color for details of black pieces */\r
3402       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3403       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3404       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3405 #endif\r
3406     }\r
3407     SelectObject(hdc, oldBrush);\r
3408     SelectObject(tmphdc, oldBitmap);\r
3409   }\r
3410 }\r
3411 \r
3412 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3413 int GetBackTextureMode( int algo )\r
3414 {\r
3415     int result = BACK_TEXTURE_MODE_DISABLED;\r
3416 \r
3417     switch( algo ) \r
3418     {\r
3419         case BACK_TEXTURE_MODE_PLAIN:\r
3420             result = 1; /* Always use identity map */\r
3421             break;\r
3422         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3423             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3424             break;\r
3425     }\r
3426 \r
3427     return result;\r
3428 }\r
3429 \r
3430 /* \r
3431     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3432     to handle redraws cleanly (as random numbers would always be different).\r
3433 */\r
3434 VOID RebuildTextureSquareInfo()\r
3435 {\r
3436     BITMAP bi;\r
3437     int lite_w = 0;\r
3438     int lite_h = 0;\r
3439     int dark_w = 0;\r
3440     int dark_h = 0;\r
3441     int row;\r
3442     int col;\r
3443 \r
3444     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3445 \r
3446     if( liteBackTexture != NULL ) {\r
3447         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3448             lite_w = bi.bmWidth;\r
3449             lite_h = bi.bmHeight;\r
3450         }\r
3451     }\r
3452 \r
3453     if( darkBackTexture != NULL ) {\r
3454         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3455             dark_w = bi.bmWidth;\r
3456             dark_h = bi.bmHeight;\r
3457         }\r
3458     }\r
3459 \r
3460     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3461         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3462             if( (col + row) & 1 ) {\r
3463                 /* Lite square */\r
3464                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3465                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3466                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3467                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3468                 }\r
3469             }\r
3470             else {\r
3471                 /* Dark square */\r
3472                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3473                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3474                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3475                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3476                 }\r
3477             }\r
3478         }\r
3479     }\r
3480 }\r
3481 \r
3482 /* [AS] Arrow highlighting support */\r
3483 \r
3484 static int A_WIDTH = 5; /* Width of arrow body */\r
3485 \r
3486 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3487 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3488 \r
3489 static double Sqr( double x )\r
3490 {\r
3491     return x*x;\r
3492 }\r
3493 \r
3494 static int Round( double x )\r
3495 {\r
3496     return (int) (x + 0.5);\r
3497 }\r
3498 \r
3499 /* Draw an arrow between two points using current settings */\r
3500 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3501 {\r
3502     POINT arrow[7];\r
3503     double dx, dy, j, k, x, y;\r
3504 \r
3505     if( d_x == s_x ) {\r
3506         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3507 \r
3508         arrow[0].x = s_x + A_WIDTH;\r
3509         arrow[0].y = s_y;\r
3510 \r
3511         arrow[1].x = s_x + A_WIDTH;\r
3512         arrow[1].y = d_y - h;\r
3513 \r
3514         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3515         arrow[2].y = d_y - h;\r
3516 \r
3517         arrow[3].x = d_x;\r
3518         arrow[3].y = d_y;\r
3519 \r
3520         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3521         arrow[4].y = d_y - h;\r
3522 \r
3523         arrow[5].x = s_x - A_WIDTH;\r
3524         arrow[5].y = d_y - h;\r
3525 \r
3526         arrow[6].x = s_x - A_WIDTH;\r
3527         arrow[6].y = s_y;\r
3528     }\r
3529     else if( d_y == s_y ) {\r
3530         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3531 \r
3532         arrow[0].x = s_x;\r
3533         arrow[0].y = s_y + A_WIDTH;\r
3534 \r
3535         arrow[1].x = d_x - w;\r
3536         arrow[1].y = s_y + A_WIDTH;\r
3537 \r
3538         arrow[2].x = d_x - w;\r
3539         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3540 \r
3541         arrow[3].x = d_x;\r
3542         arrow[3].y = d_y;\r
3543 \r
3544         arrow[4].x = d_x - w;\r
3545         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3546 \r
3547         arrow[5].x = d_x - w;\r
3548         arrow[5].y = s_y - A_WIDTH;\r
3549 \r
3550         arrow[6].x = s_x;\r
3551         arrow[6].y = s_y - A_WIDTH;\r
3552     }\r
3553     else {\r
3554         /* [AS] Needed a lot of paper for this! :-) */\r
3555         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3556         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3557   \r
3558         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3559 \r
3560         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3561 \r
3562         x = s_x;\r
3563         y = s_y;\r
3564 \r
3565         arrow[0].x = Round(x - j);\r
3566         arrow[0].y = Round(y + j*dx);\r
3567 \r
3568         arrow[1].x = Round(x + j);\r
3569         arrow[1].y = Round(y - j*dx);\r
3570 \r
3571         if( d_x > s_x ) {\r
3572             x = (double) d_x - k;\r
3573             y = (double) d_y - k*dy;\r
3574         }\r
3575         else {\r
3576             x = (double) d_x + k;\r
3577             y = (double) d_y + k*dy;\r
3578         }\r
3579 \r
3580         arrow[2].x = Round(x + j);\r
3581         arrow[2].y = Round(y - j*dx);\r
3582 \r
3583         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3584         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3585 \r
3586         arrow[4].x = d_x;\r
3587         arrow[4].y = d_y;\r
3588 \r
3589         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3590         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3591 \r
3592         arrow[6].x = Round(x - j);\r
3593         arrow[6].y = Round(y + j*dx);\r
3594     }\r
3595 \r
3596     Polygon( hdc, arrow, 7 );\r
3597 }\r
3598 \r
3599 /* [AS] Draw an arrow between two squares */\r
3600 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3601 {\r
3602     int s_x, s_y, d_x, d_y;\r
3603     HPEN hpen;\r
3604     HPEN holdpen;\r
3605     HBRUSH hbrush;\r
3606     HBRUSH holdbrush;\r
3607     LOGBRUSH stLB;\r
3608 \r
3609     if( s_col == d_col && s_row == d_row ) {\r
3610         return;\r
3611     }\r
3612 \r
3613     /* Get source and destination points */\r
3614     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3615     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3616 \r
3617     if( d_y > s_y ) {\r
3618         d_y += squareSize / 4;\r
3619     }\r
3620     else if( d_y < s_y ) {\r
3621         d_y += 3 * squareSize / 4;\r
3622     }\r
3623     else {\r
3624         d_y += squareSize / 2;\r
3625     }\r
3626 \r
3627     if( d_x > s_x ) {\r
3628         d_x += squareSize / 4;\r
3629     }\r
3630     else if( d_x < s_x ) {\r
3631         d_x += 3 * squareSize / 4;\r
3632     }\r
3633     else {\r
3634         d_x += squareSize / 2;\r
3635     }\r
3636 \r
3637     s_x += squareSize / 2;\r
3638     s_y += squareSize / 2;\r
3639 \r
3640     /* Adjust width */\r
3641     A_WIDTH = squareSize / 14;\r
3642 \r
3643     /* Draw */\r
3644     stLB.lbStyle = BS_SOLID;\r
3645     stLB.lbColor = appData.highlightArrowColor;\r
3646     stLB.lbHatch = 0;\r
3647 \r
3648     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3649     holdpen = SelectObject( hdc, hpen );\r
3650     hbrush = CreateBrushIndirect( &stLB );\r
3651     holdbrush = SelectObject( hdc, hbrush );\r
3652 \r
3653     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3654 \r
3655     SelectObject( hdc, holdpen );\r
3656     SelectObject( hdc, holdbrush );\r
3657     DeleteObject( hpen );\r
3658     DeleteObject( hbrush );\r
3659 }\r
3660 \r
3661 BOOL HasHighlightInfo()\r
3662 {\r
3663     BOOL result = FALSE;\r
3664 \r
3665     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3666         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3667     {\r
3668         result = TRUE;\r
3669     }\r
3670 \r
3671     return result;\r
3672 }\r
3673 \r
3674 BOOL IsDrawArrowEnabled()\r
3675 {\r
3676     BOOL result = FALSE;\r
3677 \r
3678     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3679         result = TRUE;\r
3680     }\r
3681 \r
3682     return result;\r
3683 }\r
3684 \r
3685 VOID DrawArrowHighlight( HDC hdc )\r
3686 {\r
3687     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3688         DrawArrowBetweenSquares( hdc,\r
3689             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3690             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3691     }\r
3692 }\r
3693 \r
3694 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3695 {\r
3696     HRGN result = NULL;\r
3697 \r
3698     if( HasHighlightInfo() ) {\r
3699         int x1, y1, x2, y2;\r
3700         int sx, sy, dx, dy;\r
3701 \r
3702         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3703         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3704 \r
3705         sx = MIN( x1, x2 );\r
3706         sy = MIN( y1, y2 );\r
3707         dx = MAX( x1, x2 ) + squareSize;\r
3708         dy = MAX( y1, y2 ) + squareSize;\r
3709 \r
3710         result = CreateRectRgn( sx, sy, dx, dy );\r
3711     }\r
3712 \r
3713     return result;\r
3714 }\r
3715 \r
3716 /*\r
3717     Warning: this function modifies the behavior of several other functions. \r
3718     \r
3719     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3720     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3721     repaint is scattered all over the place, which is not good for features such as\r
3722     "arrow highlighting" that require a full repaint of the board.\r
3723 \r
3724     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3725     user interaction, when speed is not so important) but especially to avoid errors\r
3726     in the displayed graphics.\r
3727 \r
3728     In such patched places, I always try refer to this function so there is a single\r
3729     place to maintain knowledge.\r
3730     \r
3731     To restore the original behavior, just return FALSE unconditionally.\r
3732 */\r
3733 BOOL IsFullRepaintPreferrable()\r
3734 {\r
3735     BOOL result = FALSE;\r
3736 \r
3737     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3738         /* Arrow may appear on the board */\r
3739         result = TRUE;\r
3740     }\r
3741 \r
3742     return result;\r
3743 }\r
3744 \r
3745 /* \r
3746     This function is called by DrawPosition to know whether a full repaint must\r
3747     be forced or not.\r
3748 \r
3749     Only DrawPosition may directly call this function, which makes use of \r
3750     some state information. Other function should call DrawPosition specifying \r
3751     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3752 */\r
3753 BOOL DrawPositionNeedsFullRepaint()\r
3754 {\r
3755     BOOL result = FALSE;\r
3756 \r
3757     /* \r
3758         Probably a slightly better policy would be to trigger a full repaint\r
3759         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3760         but animation is fast enough that it's difficult to notice.\r
3761     */\r
3762     if( animInfo.piece == EmptySquare ) {\r
3763         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3764             result = TRUE;\r
3765         }\r
3766     }\r
3767 \r
3768     return result;\r
3769 }\r
3770 \r
3771 VOID\r
3772 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3773 {\r
3774   int row, column, x, y, square_color, piece_color;\r
3775   ChessSquare piece;\r
3776   HBRUSH oldBrush;\r
3777   HDC texture_hdc = NULL;\r
3778 \r
3779   /* [AS] Initialize background textures if needed */\r
3780   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3781       if( backTextureSquareSize != squareSize ) {\r
3782           backTextureSquareSize = squareSize;\r
3783           RebuildTextureSquareInfo();\r
3784       }\r
3785 \r
3786       texture_hdc = CreateCompatibleDC( hdc );\r
3787   }\r
3788 \r
3789   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3790     for (column = 0; column < BOARD_WIDTH; column++) {\r
3791   \r
3792       SquareToPos(row, column, &x, &y);\r
3793 \r
3794       piece = board[row][column];\r
3795 \r
3796       square_color = ((column + row) % 2) == 1;\r
3797       if(!strcmp(appData.variant, "xiangqi") ) {\r
3798           square_color = !InPalace(row, column);\r
3799           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3800           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3801       }\r
3802       piece_color = (int) piece < (int) BlackPawn;\r
3803 \r
3804 \r
3805 #ifdef FAIRY\r
3806       /* [HGM] holdings file: light square or black */\r
3807       if(column == BOARD_LEFT-2) {\r
3808             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3809                 square_color = 1;\r
3810             else {\r
3811                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3812                 continue;\r
3813             }\r
3814       } else\r
3815       if(column == BOARD_RGHT + 1 ) {\r
3816             if( row < gameInfo.holdingsSize )\r
3817                 square_color = 1;\r
3818             else {\r
3819                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3820                 continue;\r
3821             }\r
3822       }\r
3823       if(column == BOARD_LEFT-1 ) /* left align */\r
3824             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3825       else if( column == BOARD_RGHT) /* right align */\r
3826             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3827       else\r
3828 #endif\r
3829       if (appData.monoMode) {\r
3830         if (piece == EmptySquare) {\r
3831           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3832                  square_color ? WHITENESS : BLACKNESS);\r
3833         } else {\r
3834           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3835         }\r
3836       } \r
3837       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3838           /* [AS] Draw the square using a texture bitmap */\r
3839           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3840 \r
3841           DrawTile( x, y, \r
3842               squareSize, squareSize, \r
3843               hdc, \r
3844               texture_hdc,\r
3845               backTextureSquareInfo[row][column].mode,\r
3846               backTextureSquareInfo[row][column].x,\r
3847               backTextureSquareInfo[row][column].y );\r
3848 \r
3849           SelectObject( texture_hdc, hbm );\r
3850 \r
3851           if (piece != EmptySquare) {\r
3852               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3853           }\r
3854       }\r
3855       else {\r
3856         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3857 \r
3858         oldBrush = SelectObject(hdc, brush );\r
3859         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3860         SelectObject(hdc, oldBrush);\r
3861         if (piece != EmptySquare)\r
3862           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3863       }\r
3864     }\r
3865   }\r
3866 \r
3867   if( texture_hdc != NULL ) {\r
3868     DeleteDC( texture_hdc );\r
3869   }\r
3870 }\r
3871 \r
3872 #define MAX_CLIPS 200   /* more than enough */\r
3873 \r
3874 VOID\r
3875 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3876 {\r
3877   static Board lastReq, lastDrawn;\r
3878   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3879   static int lastDrawnFlipView = 0;\r
3880   static int lastReqValid = 0, lastDrawnValid = 0;\r
3881   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3882   HDC tmphdc;\r
3883   HDC hdcmem;\r
3884   HBITMAP bufferBitmap;\r
3885   HBITMAP oldBitmap;\r
3886   RECT Rect;\r
3887   HRGN clips[MAX_CLIPS];\r
3888   ChessSquare dragged_piece = EmptySquare;\r
3889 \r
3890   /* I'm undecided on this - this function figures out whether a full\r
3891    * repaint is necessary on its own, so there's no real reason to have the\r
3892    * caller tell it that.  I think this can safely be set to FALSE - but\r
3893    * if we trust the callers not to request full repaints unnessesarily, then\r
3894    * we could skip some clipping work.  In other words, only request a full\r
3895    * redraw when the majority of pieces have changed positions (ie. flip, \r
3896    * gamestart and similar)  --Hawk\r
3897    */\r
3898   Boolean fullrepaint = repaint;\r
3899 \r
3900   if( DrawPositionNeedsFullRepaint() ) {\r
3901       fullrepaint = TRUE;\r
3902   }\r
3903 \r
3904 #if 0\r
3905   if( fullrepaint ) {\r
3906       static int repaint_count = 0;\r
3907       char buf[128];\r
3908 \r
3909       repaint_count++;\r
3910       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
3911       OutputDebugString( buf );\r
3912   }\r
3913 #endif\r
3914 \r
3915   if (board == NULL) {\r
3916     if (!lastReqValid) {\r
3917       return;\r
3918     }\r
3919     board = lastReq;\r
3920   } else {\r
3921     CopyBoard(lastReq, board);\r
3922     lastReqValid = 1;\r
3923   }\r
3924 \r
3925   if (doingSizing) {\r
3926     return;\r
3927   }\r
3928 \r
3929   if (IsIconic(hwndMain)) {\r
3930     return;\r
3931   }\r
3932 \r
3933   if (hdc == NULL) {\r
3934     hdc = GetDC(hwndMain);\r
3935     if (!appData.monoMode) {\r
3936       SelectPalette(hdc, hPal, FALSE);\r
3937       RealizePalette(hdc);\r
3938     }\r
3939     releaseDC = TRUE;\r
3940   } else {\r
3941     releaseDC = FALSE;\r
3942   }\r
3943 \r
3944 #if 0\r
3945   fprintf(debugFP, "*******************************\n"\r
3946                    "repaint = %s\n"\r
3947                    "dragInfo.from (%d,%d)\n"\r
3948                    "dragInfo.start (%d,%d)\n"\r
3949                    "dragInfo.pos (%d,%d)\n"\r
3950                    "dragInfo.lastpos (%d,%d)\n", \r
3951                     repaint ? "TRUE" : "FALSE",\r
3952                     dragInfo.from.x, dragInfo.from.y, \r
3953                     dragInfo.start.x, dragInfo.start.y,\r
3954                     dragInfo.pos.x, dragInfo.pos.y,\r
3955                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
3956   fprintf(debugFP, "prev:  ");\r
3957   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3958     for (column = 0; column < BOARD_WIDTH; column++) {\r
3959       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
3960     }\r
3961   }\r
3962   fprintf(debugFP, "\n");\r
3963   fprintf(debugFP, "board: ");\r
3964   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3965     for (column = 0; column < BOARD_WIDTH; column++) {\r
3966       fprintf(debugFP, "%d ", board[row][column]);\r
3967     }\r
3968   }\r
3969   fprintf(debugFP, "\n");\r
3970   fflush(debugFP);\r
3971 #endif\r
3972 \r
3973   /* Create some work-DCs */\r
3974   hdcmem = CreateCompatibleDC(hdc);\r
3975   tmphdc = CreateCompatibleDC(hdc);\r
3976 \r
3977   /* If dragging is in progress, we temporarely remove the piece */\r
3978   /* [HGM] or temporarily decrease count if stacked              */\r
3979   /*       !! Moved to before board compare !!                   */\r
3980   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3981     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3982     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3983             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3984         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3985     } else \r
3986     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3987             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3988         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3989     } else \r
3990         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3991   }\r
3992 \r
3993   /* Figure out which squares need updating by comparing the \r
3994    * newest board with the last drawn board and checking if\r
3995    * flipping has changed.\r
3996    */\r
3997   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3998     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3999       for (column = 0; column < BOARD_WIDTH; column++) {\r
4000         if (lastDrawn[row][column] != board[row][column]) {\r
4001           SquareToPos(row, column, &x, &y);\r
4002           clips[num_clips++] =\r
4003             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4004         }\r
4005       }\r
4006     }\r
4007     for (i=0; i<2; i++) {\r
4008       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4009           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4010         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4011             lastDrawnHighlight.sq[i].y >= 0) {\r
4012           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4013                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4014           clips[num_clips++] =\r
4015             CreateRectRgn(x - lineGap, y - lineGap, \r
4016                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4017         }\r
4018         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4019           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4020           clips[num_clips++] =\r
4021             CreateRectRgn(x - lineGap, y - lineGap, \r
4022                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4023         }\r
4024       }\r
4025     }\r
4026     for (i=0; i<2; i++) {\r
4027       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4028           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4029         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4030             lastDrawnPremove.sq[i].y >= 0) {\r
4031           SquareToPos(lastDrawnPremove.sq[i].y,\r
4032                       lastDrawnPremove.sq[i].x, &x, &y);\r
4033           clips[num_clips++] =\r
4034             CreateRectRgn(x - lineGap, y - lineGap, \r
4035                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4036         }\r
4037         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4038             premoveHighlightInfo.sq[i].y >= 0) {\r
4039           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4040                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4041           clips[num_clips++] =\r
4042             CreateRectRgn(x - lineGap, y - lineGap, \r
4043                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4044         }\r
4045       }\r
4046     }\r
4047   } else {\r
4048     fullrepaint = TRUE;\r
4049   }\r
4050 \r
4051   /* Create a buffer bitmap - this is the actual bitmap\r
4052    * being written to.  When all the work is done, we can\r
4053    * copy it to the real DC (the screen).  This avoids\r
4054    * the problems with flickering.\r
4055    */\r
4056   GetClientRect(hwndMain, &Rect);\r
4057   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4058                                         Rect.bottom-Rect.top+1);\r
4059   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4060   if (!appData.monoMode) {\r
4061     SelectPalette(hdcmem, hPal, FALSE);\r
4062   }\r
4063 \r
4064   /* Create clips for dragging */\r
4065   if (!fullrepaint) {\r
4066     if (dragInfo.from.x >= 0) {\r
4067       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4068       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4069     }\r
4070     if (dragInfo.start.x >= 0) {\r
4071       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4072       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4073     }\r
4074     if (dragInfo.pos.x >= 0) {\r
4075       x = dragInfo.pos.x - squareSize / 2;\r
4076       y = dragInfo.pos.y - squareSize / 2;\r
4077       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4078     }\r
4079     if (dragInfo.lastpos.x >= 0) {\r
4080       x = dragInfo.lastpos.x - squareSize / 2;\r
4081       y = dragInfo.lastpos.y - squareSize / 2;\r
4082       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4083     }\r
4084   }\r
4085 \r
4086   /* Are we animating a move?  \r
4087    * If so, \r
4088    *   - remove the piece from the board (temporarely)\r
4089    *   - calculate the clipping region\r
4090    */\r
4091   if (!fullrepaint) {\r
4092     if (animInfo.piece != EmptySquare) {\r
4093       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4094       x = boardRect.left + animInfo.lastpos.x;\r
4095       y = boardRect.top + animInfo.lastpos.y;\r
4096       x2 = boardRect.left + animInfo.pos.x;\r
4097       y2 = boardRect.top + animInfo.pos.y;\r
4098       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4099       /* Slight kludge.  The real problem is that after AnimateMove is\r
4100          done, the position on the screen does not match lastDrawn.\r
4101          This currently causes trouble only on e.p. captures in\r
4102          atomic, where the piece moves to an empty square and then\r
4103          explodes.  The old and new positions both had an empty square\r
4104          at the destination, but animation has drawn a piece there and\r
4105          we have to remember to erase it. */\r
4106       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4107     }\r
4108   }\r
4109 \r
4110   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4111   if (num_clips == 0)\r
4112     fullrepaint = TRUE;\r
4113 \r
4114   /* Set clipping on the memory DC */\r
4115   if (!fullrepaint) {\r
4116     SelectClipRgn(hdcmem, clips[0]);\r
4117     for (x = 1; x < num_clips; x++) {\r
4118       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4119         abort();  // this should never ever happen!\r
4120     }\r
4121   }\r
4122 \r
4123   /* Do all the drawing to the memory DC */\r
4124   DrawGridOnDC(hdcmem);\r
4125   DrawHighlightsOnDC(hdcmem);\r
4126   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4127 \r
4128   if( appData.highlightMoveWithArrow ) {\r
4129     DrawArrowHighlight(hdcmem);\r
4130   }\r
4131 \r
4132   DrawCoordsOnDC(hdcmem);\r
4133 \r
4134   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4135                  /* to make sure lastDrawn contains what is actually drawn */\r
4136 \r
4137   /* Put the dragged piece back into place and draw it (out of place!) */\r
4138     if (dragged_piece != EmptySquare) {\r
4139     /* [HGM] or restack */\r
4140     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4141                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4142     else\r
4143     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4144                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4145     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4146     x = dragInfo.pos.x - squareSize / 2;\r
4147     y = dragInfo.pos.y - squareSize / 2;\r
4148     DrawPieceOnDC(hdcmem, dragged_piece,\r
4149                   ((int) dragged_piece < (int) BlackPawn), \r
4150                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4151   }   \r
4152   \r
4153   /* Put the animated piece back into place and draw it */\r
4154   if (animInfo.piece != EmptySquare) {\r
4155     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4156     x = boardRect.left + animInfo.pos.x;\r
4157     y = boardRect.top + animInfo.pos.y;\r
4158     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4159                   ((int) animInfo.piece < (int) BlackPawn),\r
4160                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4161   }\r
4162 \r
4163   /* Release the bufferBitmap by selecting in the old bitmap \r
4164    * and delete the memory DC\r
4165    */\r
4166   SelectObject(hdcmem, oldBitmap);\r
4167   DeleteDC(hdcmem);\r
4168 \r
4169   /* Set clipping on the target DC */\r
4170   if (!fullrepaint) {\r
4171     SelectClipRgn(hdc, clips[0]);\r
4172     for (x = 1; x < num_clips; x++) {\r
4173       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4174         abort();   // this should never ever happen!\r
4175     } \r
4176   }\r
4177 \r
4178   /* Copy the new bitmap onto the screen in one go.\r
4179    * This way we avoid any flickering\r
4180    */\r
4181   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4182   BitBlt(hdc, boardRect.left, boardRect.top,\r
4183          boardRect.right - boardRect.left,\r
4184          boardRect.bottom - boardRect.top,\r
4185          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4186   SelectObject(tmphdc, oldBitmap);\r
4187 \r
4188   /* Massive cleanup */\r
4189   for (x = 0; x < num_clips; x++)\r
4190     DeleteObject(clips[x]);\r
4191 \r
4192   DeleteDC(tmphdc);\r
4193   DeleteObject(bufferBitmap);\r
4194 \r
4195   if (releaseDC) \r
4196     ReleaseDC(hwndMain, hdc);\r
4197   \r
4198   if (lastDrawnFlipView != flipView) {\r
4199     if (flipView)\r
4200       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4201     else\r
4202       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4203   }\r
4204 \r
4205 /*  CopyBoard(lastDrawn, board);*/\r
4206   lastDrawnHighlight = highlightInfo;\r
4207   lastDrawnPremove   = premoveHighlightInfo;\r
4208   lastDrawnFlipView = flipView;\r
4209   lastDrawnValid = 1;\r
4210 }\r
4211 \r
4212 \r
4213 /*---------------------------------------------------------------------------*\\r
4214 | CLIENT PAINT PROCEDURE\r
4215 |   This is the main event-handler for the WM_PAINT message.\r
4216 |\r
4217 \*---------------------------------------------------------------------------*/\r
4218 VOID\r
4219 PaintProc(HWND hwnd)\r
4220 {\r
4221   HDC         hdc;\r
4222   PAINTSTRUCT ps;\r
4223   HFONT       oldFont;\r
4224 \r
4225   if(hdc = BeginPaint(hwnd, &ps)) {\r
4226     if (IsIconic(hwnd)) {\r
4227       DrawIcon(hdc, 2, 2, iconCurrent);\r
4228     } else {\r
4229       if (!appData.monoMode) {\r
4230         SelectPalette(hdc, hPal, FALSE);\r
4231         RealizePalette(hdc);\r
4232       }\r
4233       HDCDrawPosition(hdc, 1, NULL);\r
4234       oldFont =\r
4235         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4236       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4237                  ETO_CLIPPED|ETO_OPAQUE,\r
4238                  &messageRect, messageText, strlen(messageText), NULL);\r
4239       SelectObject(hdc, oldFont);\r
4240       DisplayBothClocks();\r
4241     }\r
4242     EndPaint(hwnd,&ps);\r
4243   }\r
4244 \r
4245   return;\r
4246 }\r
4247 \r
4248 \r
4249 /*\r
4250  * If the user selects on a border boundary, return -1; if off the board,\r
4251  *   return -2.  Otherwise map the event coordinate to the square.\r
4252  * The offset boardRect.left or boardRect.top must already have been\r
4253  *   subtracted from x.\r
4254  */\r
4255 int\r
4256 EventToSquare(int x)\r
4257 {\r
4258   if (x <= 0)\r
4259     return -2;\r
4260   if (x < lineGap)\r
4261     return -1;\r
4262   x -= lineGap;\r
4263   if ((x % (squareSize + lineGap)) >= squareSize)\r
4264     return -1;\r
4265   x /= (squareSize + lineGap);\r
4266   if (x >= BOARD_SIZE)\r
4267     return -2;\r
4268   return x;\r
4269 }\r
4270 \r
4271 typedef struct {\r
4272   char piece;\r
4273   int command;\r
4274   char* name;\r
4275 } DropEnable;\r
4276 \r
4277 DropEnable dropEnables[] = {\r
4278   { 'P', DP_Pawn, "Pawn" },\r
4279   { 'N', DP_Knight, "Knight" },\r
4280   { 'B', DP_Bishop, "Bishop" },\r
4281   { 'R', DP_Rook, "Rook" },\r
4282   { 'Q', DP_Queen, "Queen" },\r
4283 };\r
4284 \r
4285 VOID\r
4286 SetupDropMenu(HMENU hmenu)\r
4287 {\r
4288   int i, count, enable;\r
4289   char *p;\r
4290   extern char white_holding[], black_holding[];\r
4291   char item[MSG_SIZ];\r
4292 \r
4293   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4294     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4295                dropEnables[i].piece);\r
4296     count = 0;\r
4297     while (p && *p++ == dropEnables[i].piece) count++;\r
4298     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4299     enable = count > 0 || !appData.testLegality\r
4300       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4301                       && !appData.icsActive);\r
4302     ModifyMenu(hmenu, dropEnables[i].command,\r
4303                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4304                dropEnables[i].command, item);\r
4305   }\r
4306 }\r
4307 \r
4308 static int fromX = -1, fromY = -1, toX, toY;\r
4309 \r
4310 /* Event handler for mouse messages */\r
4311 VOID\r
4312 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4313 {\r
4314   int x, y;\r
4315   POINT pt;\r
4316   static int recursive = 0;\r
4317   HMENU hmenu;\r
4318   BOOLEAN needsRedraw = FALSE;\r
4319   BOOLEAN saveAnimate;\r
4320   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4321   static BOOLEAN sameAgain = FALSE;\r
4322   ChessMove moveType;\r
4323 \r
4324   if (recursive) {\r
4325     if (message == WM_MBUTTONUP) {\r
4326       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4327          to the middle button: we simulate pressing the left button too!\r
4328          */\r
4329       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4330       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4331     }\r
4332     return;\r
4333   }\r
4334   recursive++;\r
4335   \r
4336   pt.x = LOWORD(lParam);\r
4337   pt.y = HIWORD(lParam);\r
4338   x = EventToSquare(pt.x - boardRect.left);\r
4339   y = EventToSquare(pt.y - boardRect.top);\r
4340   if (!flipView && y >= 0) {\r
4341     y = BOARD_HEIGHT - 1 - y;\r
4342   }\r
4343   if (flipView && x >= 0) {\r
4344     x = BOARD_WIDTH - 1 - x;\r
4345   }\r
4346 \r
4347   switch (message) {\r
4348   case WM_LBUTTONDOWN:\r
4349     ErrorPopDown();\r
4350     sameAgain = FALSE;\r
4351     if (y == -2) {\r
4352       /* Downclick vertically off board; check if on clock */\r
4353       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4354         if (gameMode == EditPosition) {\r
4355           SetWhiteToPlayEvent();\r
4356         } else if (gameMode == IcsPlayingBlack ||\r
4357                    gameMode == MachinePlaysWhite) {\r
4358           CallFlagEvent();\r
4359         } else if (gameMode == EditGame) {\r
4360           AdjustClock(flipClock, -1);\r
4361         }\r
4362       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4363         if (gameMode == EditPosition) {\r
4364           SetBlackToPlayEvent();\r
4365         } else if (gameMode == IcsPlayingWhite ||\r
4366                    gameMode == MachinePlaysBlack) {\r
4367           CallFlagEvent();\r
4368         } else if (gameMode == EditGame) {\r
4369           AdjustClock(!flipClock, -1);\r
4370         }\r
4371       }\r
4372       if (!appData.highlightLastMove) {\r
4373         ClearHighlights();\r
4374         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4375       }\r
4376       fromX = fromY = -1;\r
4377       dragInfo.start.x = dragInfo.start.y = -1;\r
4378       dragInfo.from = dragInfo.start;\r
4379       break;\r
4380     } else if (x < 0 || y < 0\r
4381       /* [HGM] block clicks between board and holdings */\r
4382               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4383               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4384               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4385         /* EditPosition, empty square, or different color piece;\r
4386            click-click move is possible */\r
4387                                ) {\r
4388       break;\r
4389     } else if (fromX == x && fromY == y) {\r
4390       /* Downclick on same square again */\r
4391       ClearHighlights();\r
4392       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4393       sameAgain = TRUE;  \r
4394     } else if (fromX != -1 &&\r
4395                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4396                                                                         ) {\r
4397       /* Downclick on different square. */\r
4398       /* [HGM] if on holdings file, should count as new first click ! */\r
4399       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4400         toX = x;\r
4401         toY = y;\r
4402         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4403            to make sure move is legal before showing promotion popup */\r
4404         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4405         if(moveType != ImpossibleMove) {\r
4406           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4407           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4408              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4409               appData.alwaysPromoteToQueen) {\r
4410                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4411                   if (!appData.highlightLastMove) {\r
4412                       ClearHighlights();\r
4413                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4414                   }\r
4415           } else\r
4416           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4417                   SetHighlights(fromX, fromY, toX, toY);\r
4418                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4419                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4420                      If promotion to Q is legal, all are legal! */\r
4421                   PromotionPopup(hwnd);\r
4422           } else {       /* not a promotion */\r
4423              if (appData.animate || appData.highlightLastMove) {\r
4424                  SetHighlights(fromX, fromY, toX, toY);\r
4425              } else {\r
4426                  ClearHighlights();\r
4427              }\r
4428              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4429              if (appData.animate && !appData.highlightLastMove) {\r
4430                   ClearHighlights();\r
4431                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4432              }\r
4433           }\r
4434         }\r
4435         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4436         fromX = fromY = -1;\r
4437         break;\r
4438       }\r
4439       ClearHighlights();\r
4440       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4441     }\r
4442     /* First downclick, or restart on a square with same color piece */\r
4443     if (!frozen && OKToStartUserMove(x, y)) {\r
4444       fromX = x;\r
4445       fromY = y;\r
4446       dragInfo.lastpos = pt;\r
4447       dragInfo.from.x = fromX;\r
4448       dragInfo.from.y = fromY;\r
4449       dragInfo.start = dragInfo.from;\r
4450       SetCapture(hwndMain);\r
4451     } else {\r
4452       fromX = fromY = -1;\r
4453       dragInfo.start.x = dragInfo.start.y = -1;\r
4454       dragInfo.from = dragInfo.start;\r
4455       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4456     }\r
4457     break;\r
4458 \r
4459   case WM_LBUTTONUP:\r
4460     ReleaseCapture();\r
4461     if (fromX == -1) break;\r
4462     if (x == fromX && y == fromY) {\r
4463       /* Upclick on same square */\r
4464       if (sameAgain) {\r
4465         /* Clicked same square twice: abort click-click move */\r
4466         fromX = fromY = -1;\r
4467         gotPremove = 0;\r
4468         ClearPremoveHighlights();\r
4469       } else {\r
4470         /* First square clicked: start click-click move */\r
4471         SetHighlights(fromX, fromY, -1, -1);\r
4472       }\r
4473       dragInfo.from.x = dragInfo.from.y = -1;\r
4474       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4475     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4476       /* Errant click; ignore */\r
4477       break;\r
4478     } else {\r
4479       /* Finish drag move. */\r
4480     if (appData.debugMode) {\r
4481         fprintf(debugFP, "release\n");\r
4482     }\r
4483       dragInfo.from.x = dragInfo.from.y = -1;\r
4484       toX = x;\r
4485       toY = y;\r
4486       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4487       appData.animate = appData.animate && !appData.animateDragging;\r
4488       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4489       if(moveType != ImpossibleMove) {\r
4490           /* [HGM] use move type to determine if move is promotion.\r
4491              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4492           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4493              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4494               appData.alwaysPromoteToQueen) \r
4495                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4496           else \r
4497           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4498                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4499                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4500         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4501       }\r
4502       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4503       appData.animate = saveAnimate;\r
4504       fromX = fromY = -1;\r
4505       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4506         ClearHighlights();\r
4507       }\r
4508       if (appData.animate || appData.animateDragging ||\r
4509           appData.highlightDragging || gotPremove) {\r
4510         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4511       }\r
4512     }\r
4513     dragInfo.start.x = dragInfo.start.y = -1; \r
4514     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4515     break;\r
4516 \r
4517   case WM_MOUSEMOVE:\r
4518     if ((appData.animateDragging || appData.highlightDragging)\r
4519         && (wParam & MK_LBUTTON)\r
4520         && dragInfo.from.x >= 0) \r
4521     {\r
4522       BOOL full_repaint = FALSE;\r
4523 \r
4524       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4525       if (appData.animateDragging) {\r
4526         dragInfo.pos = pt;\r
4527       }\r
4528       if (appData.highlightDragging) {\r
4529         SetHighlights(fromX, fromY, x, y);\r
4530         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4531             full_repaint = TRUE;\r
4532         }\r
4533       }\r
4534       \r
4535       DrawPosition( full_repaint, NULL);\r
4536       \r
4537       dragInfo.lastpos = dragInfo.pos;\r
4538     }\r
4539     break;\r
4540 \r
4541   case WM_MBUTTONDOWN:\r
4542   case WM_RBUTTONDOWN:\r
4543     ErrorPopDown();\r
4544     ReleaseCapture();\r
4545     fromX = fromY = -1;\r
4546     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4547     dragInfo.start.x = dragInfo.start.y = -1;\r
4548     dragInfo.from = dragInfo.start;\r
4549     dragInfo.lastpos = dragInfo.pos;\r
4550     if (appData.highlightDragging) {\r
4551       ClearHighlights();\r
4552     }\r
4553     if(y == -2) {\r
4554       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4555       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4556           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4557       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4558           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4559       }\r
4560     }\r
4561     DrawPosition(TRUE, NULL);\r
4562 \r
4563     switch (gameMode) {\r
4564     case EditPosition:\r
4565     case IcsExamining:\r
4566       if (x < 0 || y < 0) break;\r
4567       fromX = x;\r
4568       fromY = y;\r
4569       if (message == WM_MBUTTONDOWN) {\r
4570         buttonCount = 3;  /* even if system didn't think so */\r
4571         if (wParam & MK_SHIFT) \r
4572           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4573         else\r
4574           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4575       } else { /* message == WM_RBUTTONDOWN */\r
4576 #if 0\r
4577         if (buttonCount == 3) {\r
4578           if (wParam & MK_SHIFT) \r
4579             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4580           else\r
4581             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4582         } else {\r
4583           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4584         }\r
4585 #else\r
4586         /* Just have one menu, on the right button.  Windows users don't\r
4587            think to try the middle one, and sometimes other software steals\r
4588            it, or it doesn't really exist. */\r
4589         if(gameInfo.variant != VariantShogi)\r
4590             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4591         else\r
4592             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4593 #endif\r
4594       }\r
4595       break;\r
4596     case IcsPlayingWhite:\r
4597     case IcsPlayingBlack:\r
4598     case EditGame:\r
4599     case MachinePlaysWhite:\r
4600     case MachinePlaysBlack:\r
4601       if (appData.testLegality &&\r
4602           gameInfo.variant != VariantBughouse &&\r
4603           gameInfo.variant != VariantCrazyhouse) break;\r
4604       if (x < 0 || y < 0) break;\r
4605       fromX = x;\r
4606       fromY = y;\r
4607       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4608       SetupDropMenu(hmenu);\r
4609       MenuPopup(hwnd, pt, hmenu, -1);\r
4610       break;\r
4611     default:\r
4612       break;\r
4613     }\r
4614     break;\r
4615   }\r
4616 \r
4617   recursive--;\r
4618 }\r
4619 \r
4620 /* Preprocess messages for buttons in main window */\r
4621 LRESULT CALLBACK\r
4622 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4623 {\r
4624   int id = GetWindowLong(hwnd, GWL_ID);\r
4625   int i, dir;\r
4626 \r
4627   for (i=0; i<N_BUTTONS; i++) {\r
4628     if (buttonDesc[i].id == id) break;\r
4629   }\r
4630   if (i == N_BUTTONS) return 0;\r
4631   switch (message) {\r
4632   case WM_KEYDOWN:\r
4633     switch (wParam) {\r
4634     case VK_LEFT:\r
4635     case VK_RIGHT:\r
4636       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4637       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4638       return TRUE;\r
4639     }\r
4640     break;\r
4641   case WM_CHAR:\r
4642     switch (wParam) {\r
4643     case '\r':\r
4644       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4645       return TRUE;\r
4646     case '\t':\r
4647       if (appData.icsActive) {\r
4648         if (GetKeyState(VK_SHIFT) < 0) {\r
4649           /* shifted */\r
4650           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4651           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4652           SetFocus(h);\r
4653         } else {\r
4654           /* unshifted */\r
4655           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4656           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4657           SetFocus(h);\r
4658         }\r
4659         return TRUE;\r
4660       }\r
4661       break;\r
4662     default:\r
4663       if (appData.icsActive) {\r
4664         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4665         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4666         SetFocus(h);\r
4667         SendMessage(h, WM_CHAR, wParam, lParam);\r
4668         return TRUE;\r
4669       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4670         PopUpMoveDialog((char)wParam);\r
4671       }\r
4672       break;\r
4673     }\r
4674     break;\r
4675   }\r
4676   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4677 }\r
4678 \r
4679 /* Process messages for Promotion dialog box */\r
4680 LRESULT CALLBACK\r
4681 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4682 {\r
4683   char promoChar;\r
4684 \r
4685   switch (message) {\r
4686   case WM_INITDIALOG: /* message: initialize dialog box */\r
4687     /* Center the dialog over the application window */\r
4688     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4689     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4690       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4691        gameInfo.variant == VariantGiveaway) ?\r
4692                SW_SHOW : SW_HIDE);\r
4693     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4694     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4695        (PieceToChar(WhiteCardinal) != '.' ||\r
4696         PieceToChar(BlackCardinal) != '.'   ) ?\r
4697                SW_SHOW : SW_HIDE);\r
4698     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4699        (PieceToChar(WhiteMarshall) != '.' ||\r
4700         PieceToChar(BlackMarshall) != '.'   ) ?\r
4701                SW_SHOW : SW_HIDE);\r
4702     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4703     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4704        gameInfo.variant != VariantShogi ?\r
4705                SW_SHOW : SW_HIDE);\r
4706     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4707        gameInfo.variant != VariantShogi ?\r
4708                SW_SHOW : SW_HIDE);\r
4709     return TRUE;\r
4710 \r
4711   case WM_COMMAND: /* message: received a command */\r
4712     switch (LOWORD(wParam)) {\r
4713     case IDCANCEL:\r
4714       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4715       ClearHighlights();\r
4716       DrawPosition(FALSE, NULL);\r
4717       return TRUE;\r
4718     case PB_King:\r
4719       promoChar = PieceToChar(BlackKing);\r
4720       break;\r
4721     case PB_Queen:\r
4722       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4723       break;\r
4724     case PB_Rook:\r
4725       promoChar = PieceToChar(BlackRook);\r
4726       break;\r
4727     case PB_Bishop:\r
4728       promoChar = PieceToChar(BlackBishop);\r
4729       break;\r
4730     case PB_Chancellor:\r
4731       promoChar = PieceToChar(BlackMarshall);\r
4732       break;\r
4733     case PB_Archbishop:\r
4734       promoChar = PieceToChar(BlackCardinal);\r
4735       break;\r
4736     case PB_Knight:\r
4737       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4738       break;\r
4739     default:\r
4740       return FALSE;\r
4741     }\r
4742     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4743     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4744        only show the popup when we are already sure the move is valid or\r
4745        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4746        will figure out it is a promotion from the promoChar. */\r
4747     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
4748     if (!appData.highlightLastMove) {\r
4749       ClearHighlights();\r
4750       DrawPosition(FALSE, NULL);\r
4751     }\r
4752     return TRUE;\r
4753   }\r
4754   return FALSE;\r
4755 }\r
4756 \r
4757 /* Pop up promotion dialog */\r
4758 VOID\r
4759 PromotionPopup(HWND hwnd)\r
4760 {\r
4761   FARPROC lpProc;\r
4762 \r
4763   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4764   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4765     hwnd, (DLGPROC)lpProc);\r
4766   FreeProcInstance(lpProc);\r
4767 }\r
4768 \r
4769 /* Toggle ShowThinking */\r
4770 VOID\r
4771 ToggleShowThinking()\r
4772 {\r
4773   ShowThinkingEvent(!appData.showThinking);\r
4774 }\r
4775 \r
4776 VOID\r
4777 LoadGameDialog(HWND hwnd, char* title)\r
4778 {\r
4779   UINT number = 0;\r
4780   FILE *f;\r
4781   char fileTitle[MSG_SIZ];\r
4782   f = OpenFileDialog(hwnd, FALSE, "",\r
4783                      appData.oldSaveStyle ? "gam" : "pgn",\r
4784                      GAME_FILT,\r
4785                      title, &number, fileTitle, NULL);\r
4786   if (f != NULL) {\r
4787     cmailMsgLoaded = FALSE;\r
4788     if (number == 0) {\r
4789       int error = GameListBuild(f);\r
4790       if (error) {\r
4791         DisplayError("Cannot build game list", error);\r
4792       } else if (!ListEmpty(&gameList) &&\r
4793                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4794         GameListPopUp(f, fileTitle);\r
4795         return;\r
4796       }\r
4797       GameListDestroy();\r
4798       number = 1;\r
4799     }\r
4800     LoadGame(f, number, fileTitle, FALSE);\r
4801   }\r
4802 }\r
4803 \r
4804 VOID\r
4805 ChangedConsoleFont()\r
4806 {\r
4807   CHARFORMAT cfmt;\r
4808   CHARRANGE tmpsel, sel;\r
4809   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4810   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4811   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4812   PARAFORMAT paraf;\r
4813 \r
4814   cfmt.cbSize = sizeof(CHARFORMAT);\r
4815   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4816   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4817   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4818    * size.  This was undocumented in the version of MSVC++ that I had\r
4819    * when I wrote the code, but is apparently documented now.\r
4820    */\r
4821   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4822   cfmt.bCharSet = f->lf.lfCharSet;\r
4823   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4824   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4825   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4826   /* Why are the following seemingly needed too? */\r
4827   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4828   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4829   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4830   tmpsel.cpMin = 0;\r
4831   tmpsel.cpMax = -1; /*999999?*/\r
4832   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4833   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4834   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4835    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4836    */\r
4837   paraf.cbSize = sizeof(paraf);\r
4838   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4839   paraf.dxStartIndent = 0;\r
4840   paraf.dxOffset = WRAP_INDENT;\r
4841   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4842   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4843 }\r
4844 \r
4845 /*---------------------------------------------------------------------------*\\r
4846  *\r
4847  * Window Proc for main window\r
4848  *\r
4849 \*---------------------------------------------------------------------------*/\r
4850 \r
4851 /* Process messages for main window, etc. */\r
4852 LRESULT CALLBACK\r
4853 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4854 {\r
4855   FARPROC lpProc;\r
4856   int wmId, wmEvent;\r
4857   char *defName;\r
4858   FILE *f;\r
4859   UINT number;\r
4860   char fileTitle[MSG_SIZ];\r
4861   static SnapData sd;\r
4862 \r
4863   switch (message) {\r
4864 \r
4865   case WM_PAINT: /* message: repaint portion of window */\r
4866     PaintProc(hwnd);\r
4867     break;\r
4868 \r
4869   case WM_ERASEBKGND:\r
4870     if (IsIconic(hwnd)) {\r
4871       /* Cheat; change the message */\r
4872       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4873     } else {\r
4874       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4875     }\r
4876     break;\r
4877 \r
4878   case WM_LBUTTONDOWN:\r
4879   case WM_MBUTTONDOWN:\r
4880   case WM_RBUTTONDOWN:\r
4881   case WM_LBUTTONUP:\r
4882   case WM_MBUTTONUP:\r
4883   case WM_RBUTTONUP:\r
4884   case WM_MOUSEMOVE:\r
4885     MouseEvent(hwnd, message, wParam, lParam);\r
4886     break;\r
4887 \r
4888   case WM_CHAR:\r
4889     \r
4890     if (appData.icsActive) {\r
4891       if (wParam == '\t') {\r
4892         if (GetKeyState(VK_SHIFT) < 0) {\r
4893           /* shifted */\r
4894           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4895           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4896           SetFocus(h);\r
4897         } else {\r
4898           /* unshifted */\r
4899           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4900           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4901           SetFocus(h);\r
4902         }\r
4903       } else {\r
4904         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4905         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4906         SetFocus(h);\r
4907         SendMessage(h, message, wParam, lParam);\r
4908       }\r
4909     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4910       PopUpMoveDialog((char)wParam);\r
4911     }\r
4912     break;\r
4913 \r
4914   case WM_PALETTECHANGED:\r
4915     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4916       int nnew;\r
4917       HDC hdc = GetDC(hwndMain);\r
4918       SelectPalette(hdc, hPal, TRUE);\r
4919       nnew = RealizePalette(hdc);\r
4920       if (nnew > 0) {\r
4921         paletteChanged = TRUE;\r
4922 #if 0\r
4923         UpdateColors(hdc);\r
4924 #else\r
4925         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
4926 #endif\r
4927       }\r
4928       ReleaseDC(hwnd, hdc);\r
4929     }\r
4930     break;\r
4931 \r
4932   case WM_QUERYNEWPALETTE:\r
4933     if (!appData.monoMode /*&& paletteChanged*/) {\r
4934       int nnew;\r
4935       HDC hdc = GetDC(hwndMain);\r
4936       paletteChanged = FALSE;\r
4937       SelectPalette(hdc, hPal, FALSE);\r
4938       nnew = RealizePalette(hdc);\r
4939       if (nnew > 0) {\r
4940         InvalidateRect(hwnd, &boardRect, FALSE);\r
4941       }\r
4942       ReleaseDC(hwnd, hdc);\r
4943       return TRUE;\r
4944     }\r
4945     return FALSE;\r
4946 \r
4947   case WM_COMMAND: /* message: command from application menu */\r
4948     wmId    = LOWORD(wParam);\r
4949     wmEvent = HIWORD(wParam);\r
4950 \r
4951     switch (wmId) {\r
4952     case IDM_NewGame:\r
4953       ResetGameEvent();\r
4954       AnalysisPopDown();\r
4955       break;\r
4956 \r
4957     case IDM_NewGameFRC:\r
4958       if( NewGameFRC() == 0 ) {\r
4959         ResetGameEvent();\r
4960         AnalysisPopDown();\r
4961       }\r
4962       break;\r
4963 \r
4964     case IDM_NewVariant:\r
4965       NewVariantPopup(hwnd);\r
4966       break;\r
4967 \r
4968     case IDM_LoadGame:\r
4969       LoadGameDialog(hwnd, "Load Game from File");\r
4970       break;\r
4971 \r
4972     case IDM_LoadNextGame:\r
4973       ReloadGame(1);\r
4974       break;\r
4975 \r
4976     case IDM_LoadPrevGame:\r
4977       ReloadGame(-1);\r
4978       break;\r
4979 \r
4980     case IDM_ReloadGame:\r
4981       ReloadGame(0);\r
4982       break;\r
4983 \r
4984     case IDM_LoadPosition:\r
4985       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4986         Reset(FALSE, TRUE);\r
4987       }\r
4988       number = 1;\r
4989       f = OpenFileDialog(hwnd, FALSE, "",\r
4990                          appData.oldSaveStyle ? "pos" : "fen",\r
4991                          POSITION_FILT,\r
4992                          "Load Position from File", &number, fileTitle, NULL);\r
4993       if (f != NULL) {\r
4994         LoadPosition(f, number, fileTitle);\r
4995       }\r
4996       break;\r
4997 \r
4998     case IDM_LoadNextPosition:\r
4999       ReloadPosition(1);\r
5000       break;\r
5001 \r
5002     case IDM_LoadPrevPosition:\r
5003       ReloadPosition(-1);\r
5004       break;\r
5005 \r
5006     case IDM_ReloadPosition:\r
5007       ReloadPosition(0);\r
5008       break;\r
5009 \r
5010     case IDM_SaveGame:\r
5011       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5012       f = OpenFileDialog(hwnd, TRUE, defName,\r
5013                          appData.oldSaveStyle ? "gam" : "pgn",\r
5014                          GAME_FILT,\r
5015                          "Save Game to File", NULL, fileTitle, NULL);\r
5016       if (f != NULL) {\r
5017         SaveGame(f, 0, "");\r
5018       }\r
5019       break;\r
5020 \r
5021     case IDM_SavePosition:\r
5022       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5023       f = OpenFileDialog(hwnd, TRUE, defName,\r
5024                          appData.oldSaveStyle ? "pos" : "fen",\r
5025                          POSITION_FILT,\r
5026                          "Save Position to File", NULL, fileTitle, NULL);\r
5027       if (f != NULL) {\r
5028         SavePosition(f, 0, "");\r
5029       }\r
5030       break;\r
5031 \r
5032     case IDM_CopyGame:\r
5033       CopyGameToClipboard();\r
5034       break;\r
5035 \r
5036     case IDM_PasteGame:\r
5037       PasteGameFromClipboard();\r
5038       break;\r
5039 \r
5040     case IDM_CopyGameListToClipboard:\r
5041       CopyGameListToClipboard();\r
5042       break;\r
5043 \r
5044     /* [AS] Autodetect FEN or PGN data */\r
5045     case IDM_PasteAny:\r
5046       PasteGameOrFENFromClipboard();\r
5047       break;\r
5048 \r
5049     /* [AS] Move history */\r
5050     case IDM_ShowMoveHistory:\r
5051         if( MoveHistoryIsUp() ) {\r
5052             MoveHistoryPopDown();\r
5053         }\r
5054         else {\r
5055             MoveHistoryPopUp();\r
5056         }\r
5057         break;\r
5058 \r
5059     /* [AS] Eval graph */\r
5060     case IDM_ShowEvalGraph:\r
5061         if( EvalGraphIsUp() ) {\r
5062             EvalGraphPopDown();\r
5063         }\r
5064         else {\r
5065             EvalGraphPopUp();\r
5066         }\r
5067         break;\r
5068 \r
5069     /* [AS] Engine output */\r
5070     case IDM_ShowEngineOutput:\r
5071         if( EngineOutputIsUp() ) {\r
5072             EngineOutputPopDown();\r
5073         }\r
5074         else {\r
5075             EngineOutputPopUp();\r
5076         }\r
5077         break;\r
5078 \r
5079     /* [AS] User adjudication */\r
5080     case IDM_UserAdjudication_White:\r
5081         UserAdjudicationEvent( +1 );\r
5082         break;\r
5083 \r
5084     case IDM_UserAdjudication_Black:\r
5085         UserAdjudicationEvent( -1 );\r
5086         break;\r
5087 \r
5088     case IDM_UserAdjudication_Draw:\r
5089         UserAdjudicationEvent( 0 );\r
5090         break;\r
5091 \r
5092     /* [AS] Game list options dialog */\r
5093     case IDM_GameListOptions:\r
5094       GameListOptions();\r
5095       break;\r
5096 \r
5097     case IDM_CopyPosition:\r
5098       CopyFENToClipboard();\r
5099       break;\r
5100 \r
5101     case IDM_PastePosition:\r
5102       PasteFENFromClipboard();\r
5103       break;\r
5104 \r
5105     case IDM_MailMove:\r
5106       MailMoveEvent();\r
5107       break;\r
5108 \r
5109     case IDM_ReloadCMailMsg:\r
5110       Reset(TRUE, TRUE);\r
5111       ReloadCmailMsgEvent(FALSE);\r
5112       break;\r
5113 \r
5114     case IDM_Minimize:\r
5115       ShowWindow(hwnd, SW_MINIMIZE);\r
5116       break;\r
5117 \r
5118     case IDM_Exit:\r
5119       ExitEvent(0);\r
5120       break;\r
5121 \r
5122     case IDM_MachineWhite:\r
5123       MachineWhiteEvent();\r
5124       /*\r
5125        * refresh the tags dialog only if it's visible\r
5126        */\r
5127       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5128           char *tags;\r
5129           tags = PGNTags(&gameInfo);\r
5130           TagsPopUp(tags, CmailMsg());\r
5131           free(tags);\r
5132       }\r
5133       break;\r
5134 \r
5135     case IDM_MachineBlack:\r
5136       MachineBlackEvent();\r
5137       /*\r
5138        * refresh the tags dialog only if it's visible\r
5139        */\r
5140       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5141           char *tags;\r
5142           tags = PGNTags(&gameInfo);\r
5143           TagsPopUp(tags, CmailMsg());\r
5144           free(tags);\r
5145       }\r
5146       break;\r
5147 \r
5148     case IDM_TwoMachines:\r
5149       TwoMachinesEvent();\r
5150       /*\r
5151        * refresh the tags dialog only if it's visible\r
5152        */\r
5153       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5154           char *tags;\r
5155           tags = PGNTags(&gameInfo);\r
5156           TagsPopUp(tags, CmailMsg());\r
5157           free(tags);\r
5158       }\r
5159       break;\r
5160 \r
5161     case IDM_AnalysisMode:\r
5162       if (!first.analysisSupport) {\r
5163         char buf[MSG_SIZ];\r
5164         sprintf(buf, "%s does not support analysis", first.tidy);\r
5165         DisplayError(buf, 0);\r
5166       } else {\r
5167         if (!appData.showThinking) ToggleShowThinking();\r
5168         AnalyzeModeEvent();\r
5169       }\r
5170       break;\r
5171 \r
5172     case IDM_AnalyzeFile:\r
5173       if (!first.analysisSupport) {\r
5174         char buf[MSG_SIZ];\r
5175         sprintf(buf, "%s does not support analysis", first.tidy);\r
5176         DisplayError(buf, 0);\r
5177       } else {\r
5178         if (!appData.showThinking) ToggleShowThinking();\r
5179         AnalyzeFileEvent();\r
5180         LoadGameDialog(hwnd, "Analyze Game from File");\r
5181         AnalysisPeriodicEvent(1);\r
5182       }\r
5183       break;\r
5184 \r
5185     case IDM_IcsClient:\r
5186       IcsClientEvent();\r
5187       break;\r
5188 \r
5189     case IDM_EditGame:\r
5190       EditGameEvent();\r
5191       break;\r
5192 \r
5193     case IDM_EditPosition:\r
5194       EditPositionEvent();\r
5195       break;\r
5196 \r
5197     case IDM_Training:\r
5198       TrainingEvent();\r
5199       break;\r
5200 \r
5201     case IDM_ShowGameList:\r
5202       ShowGameListProc();\r
5203       break;\r
5204 \r
5205     case IDM_EditTags:\r
5206       EditTagsProc();\r
5207       break;\r
5208 \r
5209     case IDM_EditComment:\r
5210       if (commentDialogUp && editComment) {\r
5211         CommentPopDown();\r
5212       } else {\r
5213         EditCommentEvent();\r
5214       }\r
5215       break;\r
5216 \r
5217     case IDM_Pause:\r
5218       PauseEvent();\r
5219       break;\r
5220 \r
5221     case IDM_Accept:\r
5222       AcceptEvent();\r
5223       break;\r
5224 \r
5225     case IDM_Decline:\r
5226       DeclineEvent();\r
5227       break;\r
5228 \r
5229     case IDM_Rematch:\r
5230       RematchEvent();\r
5231       break;\r
5232 \r
5233     case IDM_CallFlag:\r
5234       CallFlagEvent();\r
5235       break;\r
5236 \r
5237     case IDM_Draw:\r
5238       DrawEvent();\r
5239       break;\r
5240 \r
5241     case IDM_Adjourn:\r
5242       AdjournEvent();\r
5243       break;\r
5244 \r
5245     case IDM_Abort:\r
5246       AbortEvent();\r
5247       break;\r
5248 \r
5249     case IDM_Resign:\r
5250       ResignEvent();\r
5251       break;\r
5252 \r
5253     case IDM_StopObserving:\r
5254       StopObservingEvent();\r
5255       break;\r
5256 \r
5257     case IDM_StopExamining:\r
5258       StopExaminingEvent();\r
5259       break;\r
5260 \r
5261     case IDM_TypeInMove:\r
5262       PopUpMoveDialog('\000');\r
5263       break;\r
5264 \r
5265     case IDM_Backward:\r
5266       BackwardEvent();\r
5267       SetFocus(hwndMain);\r
5268       break;\r
5269 \r
5270     case IDM_Forward:\r
5271       ForwardEvent();\r
5272       SetFocus(hwndMain);\r
5273       break;\r
5274 \r
5275     case IDM_ToStart:\r
5276       ToStartEvent();\r
5277       SetFocus(hwndMain);\r
5278       break;\r
5279 \r
5280     case IDM_ToEnd:\r
5281       ToEndEvent();\r
5282       SetFocus(hwndMain);\r
5283       break;\r
5284 \r
5285     case IDM_Revert:\r
5286       RevertEvent();\r
5287       break;\r
5288 \r
5289     case IDM_TruncateGame:\r
5290       TruncateGameEvent();\r
5291       break;\r
5292 \r
5293     case IDM_MoveNow:\r
5294       MoveNowEvent();\r
5295       break;\r
5296 \r
5297     case IDM_RetractMove:\r
5298       RetractMoveEvent();\r
5299       break;\r
5300 \r
5301     case IDM_FlipView:\r
5302       flipView = !flipView;\r
5303       DrawPosition(FALSE, NULL);\r
5304       break;\r
5305 \r
5306     case IDM_FlipClock:\r
5307       flipClock = !flipClock;\r
5308       DisplayBothClocks();\r
5309       break;\r
5310 \r
5311     case IDM_GeneralOptions:\r
5312       GeneralOptionsPopup(hwnd);\r
5313       DrawPosition(TRUE, NULL);\r
5314       break;\r
5315 \r
5316     case IDM_BoardOptions:\r
5317       BoardOptionsPopup(hwnd);\r
5318       break;\r
5319 \r
5320     case IDM_EnginePlayOptions:\r
5321       EnginePlayOptionsPopup(hwnd);\r
5322       break;\r
5323 \r
5324     case IDM_OptionsUCI:\r
5325       UciOptionsPopup(hwnd);\r
5326       break;\r
5327 \r
5328     case IDM_IcsOptions:\r
5329       IcsOptionsPopup(hwnd);\r
5330       break;\r
5331 \r
5332     case IDM_Fonts:\r
5333       FontsOptionsPopup(hwnd);\r
5334       break;\r
5335 \r
5336     case IDM_Sounds:\r
5337       SoundOptionsPopup(hwnd);\r
5338       break;\r
5339 \r
5340     case IDM_CommPort:\r
5341       CommPortOptionsPopup(hwnd);\r
5342       break;\r
5343 \r
5344     case IDM_LoadOptions:\r
5345       LoadOptionsPopup(hwnd);\r
5346       break;\r
5347 \r
5348     case IDM_SaveOptions:\r
5349       SaveOptionsPopup(hwnd);\r
5350       break;\r
5351 \r
5352     case IDM_TimeControl:\r
5353       TimeControlOptionsPopup(hwnd);\r
5354       break;\r
5355 \r
5356     case IDM_SaveSettings:\r
5357       SaveSettings(settingsFileName);\r
5358       break;\r
5359 \r
5360     case IDM_SaveSettingsOnExit:\r
5361       saveSettingsOnExit = !saveSettingsOnExit;\r
5362       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5363                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5364                                          MF_CHECKED : MF_UNCHECKED));\r
5365       break;\r
5366 \r
5367     case IDM_Hint:\r
5368       HintEvent();\r
5369       break;\r
5370 \r
5371     case IDM_Book:\r
5372       BookEvent();\r
5373       break;\r
5374 \r
5375     case IDM_AboutGame:\r
5376       AboutGameEvent();\r
5377       break;\r
5378 \r
5379     case IDM_Debug:\r
5380       appData.debugMode = !appData.debugMode;\r
5381       if (appData.debugMode) {\r
5382         char dir[MSG_SIZ];\r
5383         GetCurrentDirectory(MSG_SIZ, dir);\r
5384         SetCurrentDirectory(installDir);\r
5385         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5386         SetCurrentDirectory(dir);\r
5387         setbuf(debugFP, NULL);\r
5388       } else {\r
5389         fclose(debugFP);\r
5390         debugFP = NULL;\r
5391       }\r
5392       break;\r
5393 \r
5394     case IDM_HELPCONTENTS:\r
5395       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5396         MessageBox (GetFocus(),\r
5397                     "Unable to activate help",\r
5398                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5399       }\r
5400       break;\r
5401 \r
5402     case IDM_HELPSEARCH:\r
5403       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5404         MessageBox (GetFocus(),\r
5405                     "Unable to activate help",\r
5406                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5407       }\r
5408       break;\r
5409 \r
5410     case IDM_HELPHELP:\r
5411       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5412         MessageBox (GetFocus(),\r
5413                     "Unable to activate help",\r
5414                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5415       }\r
5416       break;\r
5417 \r
5418     case IDM_ABOUT:\r
5419       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5420       DialogBox(hInst, \r
5421         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5422         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5423       FreeProcInstance(lpProc);\r
5424       break;\r
5425 \r
5426     case IDM_DirectCommand1:\r
5427       AskQuestionEvent("Direct Command",\r
5428                        "Send to chess program:", "", "1");\r
5429       break;\r
5430     case IDM_DirectCommand2:\r
5431       AskQuestionEvent("Direct Command",\r
5432                        "Send to second chess program:", "", "2");\r
5433       break;\r
5434 \r
5435     case EP_WhitePawn:\r
5436       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_WhiteKnight:\r
5441       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_WhiteBishop:\r
5446       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_WhiteRook:\r
5451       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_WhiteQueen:\r
5456       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_WhiteFerz:\r
5461       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_WhiteWazir:\r
5466       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_WhiteAlfil:\r
5471       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_WhiteCannon:\r
5476       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_WhiteCardinal:\r
5481       EditPositionMenuEvent(WhiteCardinal, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_WhiteMarshall:\r
5486       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_WhiteKing:\r
5491       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_BlackPawn:\r
5496       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_BlackKnight:\r
5501       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_BlackBishop:\r
5506       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_BlackRook:\r
5511       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_BlackQueen:\r
5516       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_BlackFerz:\r
5521       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_BlackWazir:\r
5526       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_BlackAlfil:\r
5531       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_BlackCannon:\r
5536       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_BlackCardinal:\r
5541       EditPositionMenuEvent(BlackCardinal, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_BlackMarshall:\r
5546       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_BlackKing:\r
5551       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_EmptySquare:\r
5556       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_ClearBoard:\r
5561       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_White:\r
5566       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_Black:\r
5571       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_Promote:\r
5576       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_Demote:\r
5581       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case DP_Pawn:\r
5586       DropMenuEvent(WhitePawn, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case DP_Knight:\r
5591       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case DP_Bishop:\r
5596       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case DP_Rook:\r
5601       DropMenuEvent(WhiteRook, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case DP_Queen:\r
5606       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     default:\r
5611       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5612     }\r
5613     break;\r
5614 \r
5615   case WM_TIMER:\r
5616     switch (wParam) {\r
5617     case CLOCK_TIMER_ID:\r
5618       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5619       clockTimerEvent = 0;\r
5620       DecrementClocks(); /* call into back end */\r
5621       break;\r
5622     case LOAD_GAME_TIMER_ID:\r
5623       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5624       loadGameTimerEvent = 0;\r
5625       AutoPlayGameLoop(); /* call into back end */\r
5626       break;\r
5627     case ANALYSIS_TIMER_ID:\r
5628       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5629           appData.periodicUpdates) {\r
5630         AnalysisPeriodicEvent(0);\r
5631       } else {\r
5632         KillTimer(hwnd, analysisTimerEvent);\r
5633         analysisTimerEvent = 0;\r
5634       }\r
5635       break;\r
5636     case DELAYED_TIMER_ID:\r
5637       KillTimer(hwnd, delayedTimerEvent);\r
5638       delayedTimerEvent = 0;\r
5639       delayedTimerCallback();\r
5640       break;\r
5641     }\r
5642     break;\r
5643 \r
5644   case WM_USER_Input:\r
5645     InputEvent(hwnd, message, wParam, lParam);\r
5646     break;\r
5647 \r
5648   /* [AS] Also move "attached" child windows */\r
5649   case WM_WINDOWPOSCHANGING:\r
5650     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5651         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5652 \r
5653         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5654             /* Window is moving */\r
5655             RECT rcMain;\r
5656 \r
5657             GetWindowRect( hwnd, &rcMain );\r
5658             \r
5659             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5660             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5661             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5662         }\r
5663     }\r
5664     break;\r
5665 \r
5666   /* [AS] Snapping */\r
5667   case WM_ENTERSIZEMOVE:\r
5668     if (hwnd == hwndMain) {\r
5669       doingSizing = TRUE;\r
5670       lastSizing = 0;\r
5671     }\r
5672     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5673     break;\r
5674 \r
5675   case WM_SIZING:\r
5676     if (hwnd == hwndMain) {\r
5677       lastSizing = wParam;\r
5678     }\r
5679     break;\r
5680 \r
5681   case WM_MOVING:\r
5682       return OnMoving( &sd, hwnd, wParam, lParam );\r
5683 \r
5684   case WM_EXITSIZEMOVE:\r
5685     if (hwnd == hwndMain) {\r
5686       RECT client;\r
5687       doingSizing = FALSE;\r
5688       InvalidateRect(hwnd, &boardRect, FALSE);\r
5689       GetClientRect(hwnd, &client);\r
5690       ResizeBoard(client.right, client.bottom, lastSizing);\r
5691       lastSizing = 0;\r
5692     }\r
5693     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5694     break;\r
5695 \r
5696   case WM_DESTROY: /* message: window being destroyed */\r
5697     PostQuitMessage(0);\r
5698     break;\r
5699 \r
5700   case WM_CLOSE:\r
5701     if (hwnd == hwndMain) {\r
5702       ExitEvent(0);\r
5703     }\r
5704     break;\r
5705 \r
5706   default:      /* Passes it on if unprocessed */\r
5707     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5708   }\r
5709   return 0;\r
5710 }\r
5711 \r
5712 /*---------------------------------------------------------------------------*\\r
5713  *\r
5714  * Misc utility routines\r
5715  *\r
5716 \*---------------------------------------------------------------------------*/\r
5717 \r
5718 /*\r
5719  * Decent random number generator, at least not as bad as Windows\r
5720  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5721  */\r
5722 unsigned int randstate;\r
5723 \r
5724 int\r
5725 myrandom(void)\r
5726 {\r
5727   randstate = randstate * 1664525 + 1013904223;\r
5728   return (int) randstate & 0x7fffffff;\r
5729 }\r
5730 \r
5731 void\r
5732 mysrandom(unsigned int seed)\r
5733 {\r
5734   randstate = seed;\r
5735 }\r
5736 \r
5737 \r
5738 /* \r
5739  * returns TRUE if user selects a different color, FALSE otherwise \r
5740  */\r
5741 \r
5742 BOOL\r
5743 ChangeColor(HWND hwnd, COLORREF *which)\r
5744 {\r
5745   static BOOL firstTime = TRUE;\r
5746   static DWORD customColors[16];\r
5747   CHOOSECOLOR cc;\r
5748   COLORREF newcolor;\r
5749   int i;\r
5750   ColorClass ccl;\r
5751 \r
5752   if (firstTime) {\r
5753     /* Make initial colors in use available as custom colors */\r
5754     /* Should we put the compiled-in defaults here instead? */\r
5755     i = 0;\r
5756     customColors[i++] = lightSquareColor & 0xffffff;\r
5757     customColors[i++] = darkSquareColor & 0xffffff;\r
5758     customColors[i++] = whitePieceColor & 0xffffff;\r
5759     customColors[i++] = blackPieceColor & 0xffffff;\r
5760     customColors[i++] = highlightSquareColor & 0xffffff;\r
5761     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5762 \r
5763     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5764       customColors[i++] = textAttribs[ccl].color;\r
5765     }\r
5766     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5767     firstTime = FALSE;\r
5768   }\r
5769 \r
5770   cc.lStructSize = sizeof(cc);\r
5771   cc.hwndOwner = hwnd;\r
5772   cc.hInstance = NULL;\r
5773   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5774   cc.lpCustColors = (LPDWORD) customColors;\r
5775   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5776 \r
5777   if (!ChooseColor(&cc)) return FALSE;\r
5778 \r
5779   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5780   if (newcolor == *which) return FALSE;\r
5781   *which = newcolor;\r
5782   return TRUE;\r
5783 \r
5784   /*\r
5785   InitDrawingColors();\r
5786   InvalidateRect(hwnd, &boardRect, FALSE);\r
5787   */\r
5788 }\r
5789 \r
5790 BOOLEAN\r
5791 MyLoadSound(MySound *ms)\r
5792 {\r
5793   BOOL ok = FALSE;\r
5794   struct stat st;\r
5795   FILE *f;\r
5796 \r
5797   if (ms->data) free(ms->data);\r
5798   ms->data = NULL;\r
5799 \r
5800   switch (ms->name[0]) {\r
5801   case NULLCHAR:\r
5802     /* Silence */\r
5803     ok = TRUE;\r
5804     break;\r
5805   case '$':\r
5806     /* System sound from Control Panel.  Don't preload here. */\r
5807     ok = TRUE;\r
5808     break;\r
5809   case '!':\r
5810     if (ms->name[1] == NULLCHAR) {\r
5811       /* "!" alone = silence */\r
5812       ok = TRUE;\r
5813     } else {\r
5814       /* Builtin wave resource.  Error if not found. */\r
5815       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5816       if (h == NULL) break;\r
5817       ms->data = (void *)LoadResource(hInst, h);\r
5818       if (h == NULL) break;\r
5819       ok = TRUE;\r
5820     }\r
5821     break;\r
5822   default:\r
5823     /* .wav file.  Error if not found. */\r
5824     f = fopen(ms->name, "rb");\r
5825     if (f == NULL) break;\r
5826     if (fstat(fileno(f), &st) < 0) break;\r
5827     ms->data = malloc(st.st_size);\r
5828     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5829     fclose(f);\r
5830     ok = TRUE;\r
5831     break;\r
5832   }\r
5833   if (!ok) {\r
5834     char buf[MSG_SIZ];\r
5835     sprintf(buf, "Error loading sound %s", ms->name);\r
5836     DisplayError(buf, GetLastError());\r
5837   }\r
5838   return ok;\r
5839 }\r
5840 \r
5841 BOOLEAN\r
5842 MyPlaySound(MySound *ms)\r
5843 {\r
5844   BOOLEAN ok = FALSE;\r
5845   switch (ms->name[0]) {\r
5846   case NULLCHAR:\r
5847     /* Silence */\r
5848     ok = TRUE;\r
5849     break;\r
5850   case '$':\r
5851     /* System sound from Control Panel (deprecated feature).\r
5852        "$" alone or an unset sound name gets default beep (still in use). */\r
5853     if (ms->name[1]) {\r
5854       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5855     }\r
5856     if (!ok) ok = MessageBeep(MB_OK);\r
5857     break; \r
5858   case '!':\r
5859     /* Builtin wave resource, or "!" alone for silence */\r
5860     if (ms->name[1]) {\r
5861       if (ms->data == NULL) return FALSE;\r
5862       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5863     } else {\r
5864       ok = TRUE;\r
5865     }\r
5866     break;\r
5867   default:\r
5868     /* .wav file.  Error if not found. */\r
5869     if (ms->data == NULL) return FALSE;\r
5870     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5871     break;\r
5872   }\r
5873   /* Don't print an error: this can happen innocently if the sound driver\r
5874      is busy; for instance, if another instance of WinBoard is playing\r
5875      a sound at about the same time. */\r
5876 #if 0\r
5877   if (!ok) {\r
5878     char buf[MSG_SIZ];\r
5879     sprintf(buf, "Error playing sound %s", ms->name);\r
5880     DisplayError(buf, GetLastError());\r
5881   }\r
5882 #endif\r
5883   return ok;\r
5884 }\r
5885 \r
5886 \r
5887 LRESULT CALLBACK\r
5888 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5889 {\r
5890   BOOL ok;\r
5891   OPENFILENAME *ofn;\r
5892   static UINT *number; /* gross that this is static */\r
5893 \r
5894   switch (message) {\r
5895   case WM_INITDIALOG: /* message: initialize dialog box */\r
5896     /* Center the dialog over the application window */\r
5897     ofn = (OPENFILENAME *) lParam;\r
5898     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5899       number = (UINT *) ofn->lCustData;\r
5900       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5901     } else {\r
5902       number = NULL;\r
5903     }\r
5904     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5905     return FALSE;  /* Allow for further processing */\r
5906 \r
5907   case WM_COMMAND:\r
5908     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5909       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5910     }\r
5911     return FALSE;  /* Allow for further processing */\r
5912   }\r
5913   return FALSE;\r
5914 }\r
5915 \r
5916 UINT APIENTRY\r
5917 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5918 {\r
5919   static UINT *number;\r
5920   OPENFILENAME *ofname;\r
5921   OFNOTIFY *ofnot;\r
5922   switch (uiMsg) {\r
5923   case WM_INITDIALOG:\r
5924     ofname = (OPENFILENAME *)lParam;\r
5925     number = (UINT *)(ofname->lCustData);\r
5926     break;\r
5927   case WM_NOTIFY:\r
5928     ofnot = (OFNOTIFY *)lParam;\r
5929     if (ofnot->hdr.code == CDN_FILEOK) {\r
5930       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5931     }\r
5932     break;\r
5933   }\r
5934   return 0;\r
5935 }\r
5936 \r
5937 \r
5938 FILE *\r
5939 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
5940                char *nameFilt, char *dlgTitle, UINT *number,\r
5941                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5942 {\r
5943   OPENFILENAME openFileName;\r
5944   char buf1[MSG_SIZ];\r
5945   FILE *f;\r
5946 \r
5947   if (fileName == NULL) fileName = buf1;\r
5948   if (defName == NULL) {\r
5949     strcpy(fileName, "*.");\r
5950     strcat(fileName, defExt);\r
5951   } else {\r
5952     strcpy(fileName, defName);\r
5953   }\r
5954   if (fileTitle) strcpy(fileTitle, "");\r
5955   if (number) *number = 0;\r
5956 \r
5957   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5958   openFileName.hwndOwner         = hwnd;\r
5959   openFileName.hInstance         = (HANDLE) hInst;\r
5960   openFileName.lpstrFilter       = nameFilt;\r
5961   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5962   openFileName.nMaxCustFilter    = 0L;\r
5963   openFileName.nFilterIndex      = 1L;\r
5964   openFileName.lpstrFile         = fileName;\r
5965   openFileName.nMaxFile          = MSG_SIZ;\r
5966   openFileName.lpstrFileTitle    = fileTitle;\r
5967   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5968   openFileName.lpstrInitialDir   = NULL;\r
5969   openFileName.lpstrTitle        = dlgTitle;\r
5970   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5971     | (write ? 0 : OFN_FILEMUSTEXIST) \r
5972     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5973     | (oldDialog ? 0 : OFN_EXPLORER);\r
5974   openFileName.nFileOffset       = 0;\r
5975   openFileName.nFileExtension    = 0;\r
5976   openFileName.lpstrDefExt       = defExt;\r
5977   openFileName.lCustData         = (LONG) number;\r
5978   openFileName.lpfnHook          = oldDialog ?\r
5979     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5980   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5981 \r
5982   if (write ? GetSaveFileName(&openFileName) : \r
5983               GetOpenFileName(&openFileName)) {\r
5984     /* open the file */\r
5985     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
5986     if (f == NULL) {\r
5987       MessageBox(hwnd, "File open failed", NULL,\r
5988                  MB_OK|MB_ICONEXCLAMATION);\r
5989       return NULL;\r
5990     }\r
5991   } else {\r
5992     int err = CommDlgExtendedError();\r
5993     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5994     return FALSE;\r
5995   }\r
5996   return f;\r
5997 }\r
5998 \r
5999 \r
6000 \r
6001 VOID APIENTRY\r
6002 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6003 {\r
6004   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6005 \r
6006   /*\r
6007    * Get the first pop-up menu in the menu template. This is the\r
6008    * menu that TrackPopupMenu displays.\r
6009    */\r
6010   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6011 \r
6012   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6013 \r
6014   /*\r
6015    * TrackPopup uses screen coordinates, so convert the\r
6016    * coordinates of the mouse click to screen coordinates.\r
6017    */\r
6018   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6019 \r
6020   /* Draw and track the floating pop-up menu. */\r
6021   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6022                  pt.x, pt.y, 0, hwnd, NULL);\r
6023 \r
6024   /* Destroy the menu.*/\r
6025   DestroyMenu(hmenu);\r
6026 }\r
6027    \r
6028 typedef struct {\r
6029   HWND hDlg, hText;\r
6030   int sizeX, sizeY, newSizeX, newSizeY;\r
6031   HDWP hdwp;\r
6032 } ResizeEditPlusButtonsClosure;\r
6033 \r
6034 BOOL CALLBACK\r
6035 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6036 {\r
6037   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6038   RECT rect;\r
6039   POINT pt;\r
6040 \r
6041   if (hChild == cl->hText) return TRUE;\r
6042   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6043   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6044   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6045   ScreenToClient(cl->hDlg, &pt);\r
6046   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6047     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6048   return TRUE;\r
6049 }\r
6050 \r
6051 /* Resize a dialog that has a (rich) edit field filling most of\r
6052    the top, with a row of buttons below */\r
6053 VOID\r
6054 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6055 {\r
6056   RECT rectText;\r
6057   int newTextHeight, newTextWidth;\r
6058   ResizeEditPlusButtonsClosure cl;\r
6059   \r
6060   /*if (IsIconic(hDlg)) return;*/\r
6061   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6062   \r
6063   cl.hdwp = BeginDeferWindowPos(8);\r
6064 \r
6065   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6066   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6067   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6068   if (newTextHeight < 0) {\r
6069     newSizeY += -newTextHeight;\r
6070     newTextHeight = 0;\r
6071   }\r
6072   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6073     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6074 \r
6075   cl.hDlg = hDlg;\r
6076   cl.hText = hText;\r
6077   cl.sizeX = sizeX;\r
6078   cl.sizeY = sizeY;\r
6079   cl.newSizeX = newSizeX;\r
6080   cl.newSizeY = newSizeY;\r
6081   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6082 \r
6083   EndDeferWindowPos(cl.hdwp);\r
6084 }\r
6085 \r
6086 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6087 {\r
6088     RECT    rChild, rParent;\r
6089     int     wChild, hChild, wParent, hParent;\r
6090     int     wScreen, hScreen, xNew, yNew;\r
6091     HDC     hdc;\r
6092 \r
6093     /* Get the Height and Width of the child window */\r
6094     GetWindowRect (hwndChild, &rChild);\r
6095     wChild = rChild.right - rChild.left;\r
6096     hChild = rChild.bottom - rChild.top;\r
6097 \r
6098     /* Get the Height and Width of the parent window */\r
6099     GetWindowRect (hwndParent, &rParent);\r
6100     wParent = rParent.right - rParent.left;\r
6101     hParent = rParent.bottom - rParent.top;\r
6102 \r
6103     /* Get the display limits */\r
6104     hdc = GetDC (hwndChild);\r
6105     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6106     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6107     ReleaseDC(hwndChild, hdc);\r
6108 \r
6109     /* Calculate new X position, then adjust for screen */\r
6110     xNew = rParent.left + ((wParent - wChild) /2);\r
6111     if (xNew < 0) {\r
6112         xNew = 0;\r
6113     } else if ((xNew+wChild) > wScreen) {\r
6114         xNew = wScreen - wChild;\r
6115     }\r
6116 \r
6117     /* Calculate new Y position, then adjust for screen */\r
6118     if( mode == 0 ) {\r
6119         yNew = rParent.top  + ((hParent - hChild) /2);\r
6120     }\r
6121     else {\r
6122         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6123     }\r
6124 \r
6125     if (yNew < 0) {\r
6126         yNew = 0;\r
6127     } else if ((yNew+hChild) > hScreen) {\r
6128         yNew = hScreen - hChild;\r
6129     }\r
6130 \r
6131     /* Set it, and return */\r
6132     return SetWindowPos (hwndChild, NULL,\r
6133                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6134 }\r
6135 \r
6136 /* Center one window over another */\r
6137 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6138 {\r
6139     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6140 }\r
6141 \r
6142 /*---------------------------------------------------------------------------*\\r
6143  *\r
6144  * Startup Dialog functions\r
6145  *\r
6146 \*---------------------------------------------------------------------------*/\r
6147 void\r
6148 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6149 {\r
6150   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6151 \r
6152   while (*cd != NULL) {\r
6153     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6154     cd++;\r
6155   }\r
6156 }\r
6157 \r
6158 void\r
6159 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6160 {\r
6161   char buf1[ARG_MAX];\r
6162   int len;\r
6163 \r
6164   if (str[0] == '@') {\r
6165     FILE* f = fopen(str + 1, "r");\r
6166     if (f == NULL) {\r
6167       DisplayFatalError(str + 1, errno, 2);\r
6168       return;\r
6169     }\r
6170     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6171     fclose(f);\r
6172     buf1[len] = NULLCHAR;\r
6173     str = buf1;\r
6174   }\r
6175 \r
6176   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6177 \r
6178   for (;;) {\r
6179     char buf[MSG_SIZ];\r
6180     char *end = strchr(str, '\n');\r
6181     if (end == NULL) return;\r
6182     memcpy(buf, str, end - str);\r
6183     buf[end - str] = NULLCHAR;\r
6184     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6185     str = end + 1;\r
6186   }\r
6187 }\r
6188 \r
6189 void\r
6190 SetStartupDialogEnables(HWND hDlg)\r
6191 {\r
6192   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6193     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6194     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6195   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6196     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6197   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6198     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6199   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6200     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6201   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6202     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6203     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6204     IsDlgButtonChecked(hDlg, OPT_View));\r
6205 }\r
6206 \r
6207 char *\r
6208 QuoteForFilename(char *filename)\r
6209 {\r
6210   int dquote, space;\r
6211   dquote = strchr(filename, '"') != NULL;\r
6212   space = strchr(filename, ' ') != NULL;\r
6213   if (dquote || space) {\r
6214     if (dquote) {\r
6215       return "'";\r
6216     } else {\r
6217       return "\"";\r
6218     }\r
6219   } else {\r
6220     return "";\r
6221   }\r
6222 }\r
6223 \r
6224 VOID\r
6225 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6226 {\r
6227   char buf[MSG_SIZ];\r
6228   char *q;\r
6229 \r
6230   InitComboStringsFromOption(hwndCombo, nthnames);\r
6231   q = QuoteForFilename(nthcp);\r
6232   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6233   if (*nthdir != NULLCHAR) {\r
6234     q = QuoteForFilename(nthdir);\r
6235     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6236   }\r
6237   if (*nthcp == NULLCHAR) {\r
6238     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6239   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6240     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6241     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6242   }\r
6243 }\r
6244 \r
6245 LRESULT CALLBACK\r
6246 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6247 {\r
6248   char buf[MSG_SIZ];\r
6249   HANDLE hwndCombo;\r
6250   char *p;\r
6251 \r
6252   switch (message) {\r
6253   case WM_INITDIALOG:\r
6254     /* Center the dialog */\r
6255     CenterWindow (hDlg, GetDesktopWindow());\r
6256     /* Initialize the dialog items */\r
6257     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6258                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6259                   firstChessProgramNames);\r
6260     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6261                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6262                   secondChessProgramNames);\r
6263     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6264     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6265     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6266     if (*appData.icsHelper != NULLCHAR) {\r
6267       char *q = QuoteForFilename(appData.icsHelper);\r
6268       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6269     }\r
6270     if (*appData.icsHost == NULLCHAR) {\r
6271       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6272       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6273     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6274       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6275       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6276     }\r
6277 \r
6278     if (appData.icsActive) {\r
6279       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6280     }\r
6281     else if (appData.noChessProgram) {\r
6282       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6283     }\r
6284     else {\r
6285       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6286     }\r
6287 \r
6288     SetStartupDialogEnables(hDlg);\r
6289     return TRUE;\r
6290 \r
6291   case WM_COMMAND:\r
6292     switch (LOWORD(wParam)) {\r
6293     case IDOK:\r
6294       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6295         strcpy(buf, "/fcp=");\r
6296         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6297         p = buf;\r
6298         ParseArgs(StringGet, &p);\r
6299         strcpy(buf, "/scp=");\r
6300         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6301         p = buf;\r
6302         ParseArgs(StringGet, &p);\r
6303         appData.noChessProgram = FALSE;\r
6304         appData.icsActive = FALSE;\r
6305       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6306         strcpy(buf, "/ics /icshost=");\r
6307         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6308         p = buf;\r
6309         ParseArgs(StringGet, &p);\r
6310         if (appData.zippyPlay) {\r
6311           strcpy(buf, "/fcp=");\r
6312           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6313           p = buf;\r
6314           ParseArgs(StringGet, &p);\r
6315         }\r
6316       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6317         appData.noChessProgram = TRUE;\r
6318         appData.icsActive = FALSE;\r
6319       } else {\r
6320         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6321                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6322         return TRUE;\r
6323       }\r
6324       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6325         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6326         p = buf;\r
6327         ParseArgs(StringGet, &p);\r
6328       }\r
6329       EndDialog(hDlg, TRUE);\r
6330       return TRUE;\r
6331 \r
6332     case IDCANCEL:\r
6333       ExitEvent(0);\r
6334       return TRUE;\r
6335 \r
6336     case IDM_HELPCONTENTS:\r
6337       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6338         MessageBox (GetFocus(),\r
6339                     "Unable to activate help",\r
6340                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6341       }\r
6342       break;\r
6343 \r
6344     default:\r
6345       SetStartupDialogEnables(hDlg);\r
6346       break;\r
6347     }\r
6348     break;\r
6349   }\r
6350   return FALSE;\r
6351 }\r
6352 \r
6353 /*---------------------------------------------------------------------------*\\r
6354  *\r
6355  * About box dialog functions\r
6356  *\r
6357 \*---------------------------------------------------------------------------*/\r
6358 \r
6359 /* Process messages for "About" dialog box */\r
6360 LRESULT CALLBACK\r
6361 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6362 {\r
6363   switch (message) {\r
6364   case WM_INITDIALOG: /* message: initialize dialog box */\r
6365     /* Center the dialog over the application window */\r
6366     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6367     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6368     return (TRUE);\r
6369 \r
6370   case WM_COMMAND: /* message: received a command */\r
6371     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6372         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6373       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6374       return (TRUE);\r
6375     }\r
6376     break;\r
6377   }\r
6378   return (FALSE);\r
6379 }\r
6380 \r
6381 /*---------------------------------------------------------------------------*\\r
6382  *\r
6383  * Comment Dialog functions\r
6384  *\r
6385 \*---------------------------------------------------------------------------*/\r
6386 \r
6387 LRESULT CALLBACK\r
6388 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6389 {\r
6390   static HANDLE hwndText = NULL;\r
6391   int len, newSizeX, newSizeY, flags;\r
6392   static int sizeX, sizeY;\r
6393   char *str;\r
6394   RECT rect;\r
6395   MINMAXINFO *mmi;\r
6396 \r
6397   switch (message) {\r
6398   case WM_INITDIALOG: /* message: initialize dialog box */\r
6399     /* Initialize the dialog items */\r
6400     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6401     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6402     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6403     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6404     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6405     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6406     SetWindowText(hDlg, commentTitle);\r
6407     if (editComment) {\r
6408       SetFocus(hwndText);\r
6409     } else {\r
6410       SetFocus(GetDlgItem(hDlg, IDOK));\r
6411     }\r
6412     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6413                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6414                 MAKELPARAM(FALSE, 0));\r
6415     /* Size and position the dialog */\r
6416     if (!commentDialog) {\r
6417       commentDialog = hDlg;\r
6418       flags = SWP_NOZORDER;\r
6419       GetClientRect(hDlg, &rect);\r
6420       sizeX = rect.right;\r
6421       sizeY = rect.bottom;\r
6422       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6423           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6424         WINDOWPLACEMENT wp;\r
6425         EnsureOnScreen(&commentX, &commentY);\r
6426         wp.length = sizeof(WINDOWPLACEMENT);\r
6427         wp.flags = 0;\r
6428         wp.showCmd = SW_SHOW;\r
6429         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6430         wp.rcNormalPosition.left = commentX;\r
6431         wp.rcNormalPosition.right = commentX + commentW;\r
6432         wp.rcNormalPosition.top = commentY;\r
6433         wp.rcNormalPosition.bottom = commentY + commentH;\r
6434         SetWindowPlacement(hDlg, &wp);\r
6435 \r
6436         GetClientRect(hDlg, &rect);\r
6437         newSizeX = rect.right;\r
6438         newSizeY = rect.bottom;\r
6439         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6440                               newSizeX, newSizeY);\r
6441         sizeX = newSizeX;\r
6442         sizeY = newSizeY;\r
6443       }\r
6444     }\r
6445     return FALSE;\r
6446 \r
6447   case WM_COMMAND: /* message: received a command */\r
6448     switch (LOWORD(wParam)) {\r
6449     case IDOK:\r
6450       if (editComment) {\r
6451         char *p, *q;\r
6452         /* Read changed options from the dialog box */\r
6453         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6454         len = GetWindowTextLength(hwndText);\r
6455         str = (char *) malloc(len + 1);\r
6456         GetWindowText(hwndText, str, len + 1);\r
6457         p = q = str;\r
6458         while (*q) {\r
6459           if (*q == '\r')\r
6460             q++;\r
6461           else\r
6462             *p++ = *q++;\r
6463         }\r
6464         *p = NULLCHAR;\r
6465         ReplaceComment(commentIndex, str);\r
6466         free(str);\r
6467       }\r
6468       CommentPopDown();\r
6469       return TRUE;\r
6470 \r
6471     case IDCANCEL:\r
6472     case OPT_CancelComment:\r
6473       CommentPopDown();\r
6474       return TRUE;\r
6475 \r
6476     case OPT_ClearComment:\r
6477       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6478       break;\r
6479 \r
6480     case OPT_EditComment:\r
6481       EditCommentEvent();\r
6482       return TRUE;\r
6483 \r
6484     default:\r
6485       break;\r
6486     }\r
6487     break;\r
6488 \r
6489   case WM_SIZE:\r
6490     newSizeX = LOWORD(lParam);\r
6491     newSizeY = HIWORD(lParam);\r
6492     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6493     sizeX = newSizeX;\r
6494     sizeY = newSizeY;\r
6495     break;\r
6496 \r
6497   case WM_GETMINMAXINFO:\r
6498     /* Prevent resizing window too small */\r
6499     mmi = (MINMAXINFO *) lParam;\r
6500     mmi->ptMinTrackSize.x = 100;\r
6501     mmi->ptMinTrackSize.y = 100;\r
6502     break;\r
6503   }\r
6504   return FALSE;\r
6505 }\r
6506 \r
6507 VOID\r
6508 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6509 {\r
6510   FARPROC lpProc;\r
6511   char *p, *q;\r
6512 \r
6513   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6514 \r
6515   if (str == NULL) str = "";\r
6516   p = (char *) malloc(2 * strlen(str) + 2);\r
6517   q = p;\r
6518   while (*str) {\r
6519     if (*str == '\n') *q++ = '\r';\r
6520     *q++ = *str++;\r
6521   }\r
6522   *q = NULLCHAR;\r
6523   if (commentText != NULL) free(commentText);\r
6524 \r
6525   commentIndex = index;\r
6526   commentTitle = title;\r
6527   commentText = p;\r
6528   editComment = edit;\r
6529 \r
6530   if (commentDialog) {\r
6531     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6532     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6533   } else {\r
6534     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6535     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6536                  hwndMain, (DLGPROC)lpProc);\r
6537     FreeProcInstance(lpProc);\r
6538   }\r
6539   commentDialogUp = TRUE;\r
6540 }\r
6541 \r
6542 \r
6543 /*---------------------------------------------------------------------------*\\r
6544  *\r
6545  * Type-in move dialog functions\r
6546  * \r
6547 \*---------------------------------------------------------------------------*/\r
6548 \r
6549 LRESULT CALLBACK\r
6550 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6551 {\r
6552   char move[MSG_SIZ];\r
6553   HWND hInput;\r
6554   ChessMove moveType;\r
6555   int fromX, fromY, toX, toY;\r
6556   char promoChar;\r
6557 \r
6558   switch (message) {\r
6559   case WM_INITDIALOG:\r
6560     move[0] = (char) lParam;\r
6561     move[1] = NULLCHAR;\r
6562     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6563     hInput = GetDlgItem(hDlg, OPT_Move);\r
6564     SetWindowText(hInput, move);\r
6565     SetFocus(hInput);\r
6566     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6567     return FALSE;\r
6568 \r
6569   case WM_COMMAND:\r
6570     switch (LOWORD(wParam)) {\r
6571     case IDOK:\r
6572       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6573         gameMode != Training) {\r
6574         DisplayMoveError("Displayed move is not current");\r
6575       } else {\r
6576         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6577         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6578           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6579           if (gameMode != Training)\r
6580               forwardMostMove = currentMove;\r
6581           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6582         } else {\r
6583           DisplayMoveError("Could not parse move");\r
6584         }\r
6585       }\r
6586       EndDialog(hDlg, TRUE);\r
6587       return TRUE;\r
6588     case IDCANCEL:\r
6589       EndDialog(hDlg, FALSE);\r
6590       return TRUE;\r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595   }\r
6596   return FALSE;\r
6597 }\r
6598 \r
6599 VOID\r
6600 PopUpMoveDialog(char firstchar)\r
6601 {\r
6602     FARPROC lpProc;\r
6603     \r
6604     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6605         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6606         gameMode == AnalyzeMode || gameMode == EditGame || \r
6607         gameMode == EditPosition || gameMode == IcsExamining ||\r
6608         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6609         gameMode == Training) {\r
6610       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6611       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6612         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6613       FreeProcInstance(lpProc);\r
6614     }\r
6615 }\r
6616 \r
6617 /*---------------------------------------------------------------------------*\\r
6618  *\r
6619  *  Error dialogs\r
6620  * \r
6621 \*---------------------------------------------------------------------------*/\r
6622 \r
6623 /* Nonmodal error box */\r
6624 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6625                              WPARAM wParam, LPARAM lParam);\r
6626 \r
6627 VOID\r
6628 ErrorPopUp(char *title, char *content)\r
6629 {\r
6630   FARPROC lpProc;\r
6631   char *p, *q;\r
6632   BOOLEAN modal = hwndMain == NULL;\r
6633 \r
6634   p = content;\r
6635   q = errorMessage;\r
6636   while (*p) {\r
6637     if (*p == '\n') {\r
6638       if (modal) {\r
6639         *q++ = ' ';\r
6640         p++;\r
6641       } else {\r
6642         *q++ = '\r';\r
6643         *q++ = *p++;\r
6644       }\r
6645     } else {\r
6646       *q++ = *p++;\r
6647     }\r
6648   }\r
6649   *q = NULLCHAR;\r
6650   strncpy(errorTitle, title, sizeof(errorTitle));\r
6651   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6652   \r
6653   if (modal) {\r
6654     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6655   } else {\r
6656     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6657     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6658                  hwndMain, (DLGPROC)lpProc);\r
6659     FreeProcInstance(lpProc);\r
6660   }\r
6661 }\r
6662 \r
6663 VOID\r
6664 ErrorPopDown()\r
6665 {\r
6666   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6667   if (errorDialog == NULL) return;\r
6668   DestroyWindow(errorDialog);\r
6669   errorDialog = NULL;\r
6670 }\r
6671 \r
6672 LRESULT CALLBACK\r
6673 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6674 {\r
6675   HANDLE hwndText;\r
6676   RECT rChild;\r
6677 \r
6678   switch (message) {\r
6679   case WM_INITDIALOG:\r
6680     GetWindowRect(hDlg, &rChild);\r
6681 \r
6682     /*\r
6683     SetWindowPos(hDlg, NULL, rChild.left,\r
6684       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6685       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6686     */\r
6687 \r
6688     /* \r
6689         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6690         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6691         and it doesn't work when you resize the dialog.\r
6692         For now, just give it a default position.\r
6693     */\r
6694     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6695 \r
6696     errorDialog = hDlg;\r
6697     SetWindowText(hDlg, errorTitle);\r
6698     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6699     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6700     return FALSE;\r
6701 \r
6702   case WM_COMMAND:\r
6703     switch (LOWORD(wParam)) {\r
6704     case IDOK:\r
6705     case IDCANCEL:\r
6706       if (errorDialog == hDlg) errorDialog = NULL;\r
6707       DestroyWindow(hDlg);\r
6708       return TRUE;\r
6709 \r
6710     default:\r
6711       break;\r
6712     }\r
6713     break;\r
6714   }\r
6715   return FALSE;\r
6716 }\r
6717 \r
6718 #ifdef GOTHIC\r
6719 HWND gothicDialog = NULL;\r
6720 \r
6721 LRESULT CALLBACK\r
6722 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6723 {\r
6724   HANDLE hwndText;\r
6725   RECT rChild;\r
6726   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6727 \r
6728   switch (message) {\r
6729   case WM_INITDIALOG:\r
6730     GetWindowRect(hDlg, &rChild);\r
6731 \r
6732     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6733                                                              SWP_NOZORDER);\r
6734 \r
6735     /* \r
6736         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6737         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6738         and it doesn't work when you resize the dialog.\r
6739         For now, just give it a default position.\r
6740     */\r
6741     gothicDialog = hDlg;\r
6742     SetWindowText(hDlg, errorTitle);\r
6743     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6744     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6745     return FALSE;\r
6746 \r
6747   case WM_COMMAND:\r
6748     switch (LOWORD(wParam)) {\r
6749     case IDOK:\r
6750     case IDCANCEL:\r
6751       if (errorDialog == hDlg) errorDialog = NULL;\r
6752       DestroyWindow(hDlg);\r
6753       return TRUE;\r
6754 \r
6755     default:\r
6756       break;\r
6757     }\r
6758     break;\r
6759   }\r
6760   return FALSE;\r
6761 }\r
6762 \r
6763 VOID\r
6764 GothicPopUp(char *title, char up)\r
6765 {\r
6766   FARPROC lpProc;\r
6767   char *p, *q;\r
6768   BOOLEAN modal = hwndMain == NULL;\r
6769 \r
6770   strncpy(errorTitle, title, sizeof(errorTitle));\r
6771   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6772 \r
6773   if(up && gothicDialog == NULL) {\r
6774     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6775     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6776                  hwndMain, (DLGPROC)lpProc);\r
6777     FreeProcInstance(lpProc);\r
6778   } else if(!up && gothicDialog != NULL) {\r
6779     DestroyWindow(gothicDialog);\r
6780     gothicDialog = NULL;\r
6781   }\r
6782 }\r
6783 #endif\r
6784 \r
6785 /*---------------------------------------------------------------------------*\\r
6786  *\r
6787  *  Ics Interaction console functions\r
6788  *\r
6789 \*---------------------------------------------------------------------------*/\r
6790 \r
6791 #define HISTORY_SIZE 64\r
6792 static char *history[HISTORY_SIZE];\r
6793 int histIn = 0, histP = 0;\r
6794 \r
6795 VOID\r
6796 SaveInHistory(char *cmd)\r
6797 {\r
6798   if (history[histIn] != NULL) {\r
6799     free(history[histIn]);\r
6800     history[histIn] = NULL;\r
6801   }\r
6802   if (*cmd == NULLCHAR) return;\r
6803   history[histIn] = StrSave(cmd);\r
6804   histIn = (histIn + 1) % HISTORY_SIZE;\r
6805   if (history[histIn] != NULL) {\r
6806     free(history[histIn]);\r
6807     history[histIn] = NULL;\r
6808   }\r
6809   histP = histIn;\r
6810 }\r
6811 \r
6812 char *\r
6813 PrevInHistory(char *cmd)\r
6814 {\r
6815   int newhp;\r
6816   if (histP == histIn) {\r
6817     if (history[histIn] != NULL) free(history[histIn]);\r
6818     history[histIn] = StrSave(cmd);\r
6819   }\r
6820   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6821   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6822   histP = newhp;\r
6823   return history[histP];\r
6824 }\r
6825 \r
6826 char *\r
6827 NextInHistory()\r
6828 {\r
6829   if (histP == histIn) return NULL;\r
6830   histP = (histP + 1) % HISTORY_SIZE;\r
6831   return history[histP];\r
6832 }\r
6833 \r
6834 typedef struct {\r
6835   char *item;\r
6836   char *command;\r
6837   BOOLEAN getname;\r
6838   BOOLEAN immediate;\r
6839 } IcsTextMenuEntry;\r
6840 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6841 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6842 \r
6843 void\r
6844 ParseIcsTextMenu(char *icsTextMenuString)\r
6845 {\r
6846   int flags = 0;\r
6847   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6848   char *p = icsTextMenuString;\r
6849   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6850     free(e->item);\r
6851     e->item = NULL;\r
6852     if (e->command != NULL) {\r
6853       free(e->command);\r
6854       e->command = NULL;\r
6855     }\r
6856     e++;\r
6857   }\r
6858   e = icsTextMenuEntry;\r
6859   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6860     if (*p == ';' || *p == '\n') {\r
6861       e->item = strdup("-");\r
6862       e->command = NULL;\r
6863       p++;\r
6864     } else if (*p == '-') {\r
6865       e->item = strdup("-");\r
6866       e->command = NULL;\r
6867       p++;\r
6868       if (*p) p++;\r
6869     } else {\r
6870       char *q, *r, *s, *t;\r
6871       char c;\r
6872       q = strchr(p, ',');\r
6873       if (q == NULL) break;\r
6874       *q = NULLCHAR;\r
6875       r = strchr(q + 1, ',');\r
6876       if (r == NULL) break;\r
6877       *r = NULLCHAR;\r
6878       s = strchr(r + 1, ',');\r
6879       if (s == NULL) break;\r
6880       *s = NULLCHAR;\r
6881       c = ';';\r
6882       t = strchr(s + 1, c);\r
6883       if (t == NULL) {\r
6884         c = '\n';\r
6885         t = strchr(s + 1, c);\r
6886       }\r
6887       if (t != NULL) *t = NULLCHAR;\r
6888       e->item = strdup(p);\r
6889       e->command = strdup(q + 1);\r
6890       e->getname = *(r + 1) != '0';\r
6891       e->immediate = *(s + 1) != '0';\r
6892       *q = ',';\r
6893       *r = ',';\r
6894       *s = ',';\r
6895       if (t == NULL) break;\r
6896       *t = c;\r
6897       p = t + 1;\r
6898     }\r
6899     e++;\r
6900   } \r
6901 }\r
6902 \r
6903 HMENU\r
6904 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6905 {\r
6906   HMENU hmenu, h;\r
6907   int i = 0;\r
6908   hmenu = LoadMenu(hInst, "TextMenu");\r
6909   h = GetSubMenu(hmenu, 0);\r
6910   while (e->item) {\r
6911     if (strcmp(e->item, "-") == 0) {\r
6912       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6913     } else {\r
6914       if (e->item[0] == '|') {\r
6915         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6916                    IDM_CommandX + i, &e->item[1]);\r
6917       } else {\r
6918         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6919       }\r
6920     }\r
6921     e++;\r
6922     i++;\r
6923   } \r
6924   return hmenu;\r
6925 }\r
6926 \r
6927 WNDPROC consoleTextWindowProc;\r
6928 \r
6929 void\r
6930 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6931 {\r
6932   char buf[MSG_SIZ], name[MSG_SIZ];\r
6933   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6934   CHARRANGE sel;\r
6935 \r
6936   if (!getname) {\r
6937     SetWindowText(hInput, command);\r
6938     if (immediate) {\r
6939       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6940     } else {\r
6941       sel.cpMin = 999999;\r
6942       sel.cpMax = 999999;\r
6943       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6944       SetFocus(hInput);\r
6945     }\r
6946     return;\r
6947   }    \r
6948   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6949   if (sel.cpMin == sel.cpMax) {\r
6950     /* Expand to surrounding word */\r
6951     TEXTRANGE tr;\r
6952     do {\r
6953       tr.chrg.cpMax = sel.cpMin;\r
6954       tr.chrg.cpMin = --sel.cpMin;\r
6955       if (sel.cpMin < 0) break;\r
6956       tr.lpstrText = name;\r
6957       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6958     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6959     sel.cpMin++;\r
6960 \r
6961     do {\r
6962       tr.chrg.cpMin = sel.cpMax;\r
6963       tr.chrg.cpMax = ++sel.cpMax;\r
6964       tr.lpstrText = name;\r
6965       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6966     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6967     sel.cpMax--;\r
6968 \r
6969     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6970       MessageBeep(MB_ICONEXCLAMATION);\r
6971       return;\r
6972     }\r
6973     tr.chrg = sel;\r
6974     tr.lpstrText = name;\r
6975     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6976   } else {\r
6977     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6978       MessageBeep(MB_ICONEXCLAMATION);\r
6979       return;\r
6980     }\r
6981     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6982   }\r
6983   if (immediate) {\r
6984     sprintf(buf, "%s %s", command, name);\r
6985     SetWindowText(hInput, buf);\r
6986     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6987   } else {\r
6988     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6989     SetWindowText(hInput, buf);\r
6990     sel.cpMin = 999999;\r
6991     sel.cpMax = 999999;\r
6992     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6993     SetFocus(hInput);\r
6994   }\r
6995 }\r
6996 \r
6997 LRESULT CALLBACK \r
6998 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6999 {\r
7000   HWND hInput;\r
7001   CHARRANGE sel;\r
7002 \r
7003   switch (message) {\r
7004   case WM_KEYDOWN:\r
7005     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7006     switch (wParam) {\r
7007     case VK_PRIOR:\r
7008       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7009       return 0;\r
7010     case VK_NEXT:\r
7011       sel.cpMin = 999999;\r
7012       sel.cpMax = 999999;\r
7013       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7014       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7015       return 0;\r
7016     }\r
7017     break;\r
7018   case WM_CHAR:\r
7019     if (wParam == '\t') {\r
7020       if (GetKeyState(VK_SHIFT) < 0) {\r
7021         /* shifted */\r
7022         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7023         if (buttonDesc[0].hwnd) {\r
7024           SetFocus(buttonDesc[0].hwnd);\r
7025         } else {\r
7026           SetFocus(hwndMain);\r
7027         }\r
7028       } else {\r
7029         /* unshifted */\r
7030         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7031       }\r
7032     } else {\r
7033       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7034       SetFocus(hInput);\r
7035       SendMessage(hInput, message, wParam, lParam);\r
7036     }\r
7037     return 0;\r
7038   case WM_PASTE:\r
7039     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7040     SetFocus(hInput);\r
7041     return SendMessage(hInput, message, wParam, lParam);\r
7042   case WM_MBUTTONDOWN:\r
7043     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7044   case WM_RBUTTONDOWN:\r
7045     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7046       /* Move selection here if it was empty */\r
7047       POINT pt;\r
7048       pt.x = LOWORD(lParam);\r
7049       pt.y = HIWORD(lParam);\r
7050       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7051       if (sel.cpMin == sel.cpMax) {\r
7052         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7053         sel.cpMax = sel.cpMin;\r
7054         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7055       }\r
7056       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7057     }\r
7058     return 0;\r
7059   case WM_RBUTTONUP:\r
7060     if (GetKeyState(VK_SHIFT) & ~1) {\r
7061       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7062         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7063     } else {\r
7064       POINT pt;\r
7065       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7066       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7067       if (sel.cpMin == sel.cpMax) {\r
7068         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7069         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7070       }\r
7071       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7072         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7073       }\r
7074       pt.x = LOWORD(lParam);\r
7075       pt.y = HIWORD(lParam);\r
7076       MenuPopup(hwnd, pt, hmenu, -1);\r
7077     }\r
7078     return 0;\r
7079   case WM_COMMAND:\r
7080     switch (LOWORD(wParam)) {\r
7081     case IDM_QuickPaste:\r
7082       {\r
7083         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7084         if (sel.cpMin == sel.cpMax) {\r
7085           MessageBeep(MB_ICONEXCLAMATION);\r
7086           return 0;\r
7087         }\r
7088         SendMessage(hwnd, WM_COPY, 0, 0);\r
7089         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7090         SendMessage(hInput, WM_PASTE, 0, 0);\r
7091         SetFocus(hInput);\r
7092         return 0;\r
7093       }\r
7094     case IDM_Cut:\r
7095       SendMessage(hwnd, WM_CUT, 0, 0);\r
7096       return 0;\r
7097     case IDM_Paste:\r
7098       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7099       return 0;\r
7100     case IDM_Copy:\r
7101       SendMessage(hwnd, WM_COPY, 0, 0);\r
7102       return 0;\r
7103     default:\r
7104       {\r
7105         int i = LOWORD(wParam) - IDM_CommandX;\r
7106         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7107             icsTextMenuEntry[i].command != NULL) {\r
7108           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7109                    icsTextMenuEntry[i].getname,\r
7110                    icsTextMenuEntry[i].immediate);\r
7111           return 0;\r
7112         }\r
7113       }\r
7114       break;\r
7115     }\r
7116     break;\r
7117   }\r
7118   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7119 }\r
7120 \r
7121 WNDPROC consoleInputWindowProc;\r
7122 \r
7123 LRESULT CALLBACK\r
7124 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7125 {\r
7126   char buf[MSG_SIZ];\r
7127   char *p;\r
7128   static BOOL sendNextChar = FALSE;\r
7129   static BOOL quoteNextChar = FALSE;\r
7130   InputSource *is = consoleInputSource;\r
7131   CHARFORMAT cf;\r
7132   CHARRANGE sel;\r
7133 \r
7134   switch (message) {\r
7135   case WM_CHAR:\r
7136     if (!appData.localLineEditing || sendNextChar) {\r
7137       is->buf[0] = (CHAR) wParam;\r
7138       is->count = 1;\r
7139       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7140       sendNextChar = FALSE;\r
7141       return 0;\r
7142     }\r
7143     if (quoteNextChar) {\r
7144       buf[0] = (char) wParam;\r
7145       buf[1] = NULLCHAR;\r
7146       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7147       quoteNextChar = FALSE;\r
7148       return 0;\r
7149     }\r
7150     switch (wParam) {\r
7151     case '\r':   /* Enter key */\r
7152       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7153       if (consoleEcho) SaveInHistory(is->buf);\r
7154       is->buf[is->count++] = '\n';\r
7155       is->buf[is->count] = NULLCHAR;\r
7156       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7157       if (consoleEcho) {\r
7158         ConsoleOutput(is->buf, is->count, TRUE);\r
7159       } else if (appData.localLineEditing) {\r
7160         ConsoleOutput("\n", 1, TRUE);\r
7161       }\r
7162       /* fall thru */\r
7163     case '\033': /* Escape key */\r
7164       SetWindowText(hwnd, "");\r
7165       cf.cbSize = sizeof(CHARFORMAT);\r
7166       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7167       if (consoleEcho) {\r
7168         cf.crTextColor = textAttribs[ColorNormal].color;\r
7169       } else {\r
7170         cf.crTextColor = COLOR_ECHOOFF;\r
7171       }\r
7172       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7173       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7174       return 0;\r
7175     case '\t':   /* Tab key */\r
7176       if (GetKeyState(VK_SHIFT) < 0) {\r
7177         /* shifted */\r
7178         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7179       } else {\r
7180         /* unshifted */\r
7181         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7182         if (buttonDesc[0].hwnd) {\r
7183           SetFocus(buttonDesc[0].hwnd);\r
7184         } else {\r
7185           SetFocus(hwndMain);\r
7186         }\r
7187       }\r
7188       return 0;\r
7189     case '\023': /* Ctrl+S */\r
7190       sendNextChar = TRUE;\r
7191       return 0;\r
7192     case '\021': /* Ctrl+Q */\r
7193       quoteNextChar = TRUE;\r
7194       return 0;\r
7195     default:\r
7196       break;\r
7197     }\r
7198     break;\r
7199   case WM_KEYDOWN:\r
7200     switch (wParam) {\r
7201     case VK_UP:\r
7202       GetWindowText(hwnd, buf, MSG_SIZ);\r
7203       p = PrevInHistory(buf);\r
7204       if (p != NULL) {\r
7205         SetWindowText(hwnd, p);\r
7206         sel.cpMin = 999999;\r
7207         sel.cpMax = 999999;\r
7208         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7209         return 0;\r
7210       }\r
7211       break;\r
7212     case VK_DOWN:\r
7213       p = NextInHistory();\r
7214       if (p != NULL) {\r
7215         SetWindowText(hwnd, p);\r
7216         sel.cpMin = 999999;\r
7217         sel.cpMax = 999999;\r
7218         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7219         return 0;\r
7220       }\r
7221       break;\r
7222     case VK_HOME:\r
7223     case VK_END:\r
7224       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7225       /* fall thru */\r
7226     case VK_PRIOR:\r
7227     case VK_NEXT:\r
7228       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7229       return 0;\r
7230     }\r
7231     break;\r
7232   case WM_MBUTTONDOWN:\r
7233     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7234       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7235     break;\r
7236   case WM_RBUTTONUP:\r
7237     if (GetKeyState(VK_SHIFT) & ~1) {\r
7238       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7239         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7240     } else {\r
7241       POINT pt;\r
7242       HMENU hmenu;\r
7243       hmenu = LoadMenu(hInst, "InputMenu");\r
7244       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7245       if (sel.cpMin == sel.cpMax) {\r
7246         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7247         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7248       }\r
7249       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7250         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7251       }\r
7252       pt.x = LOWORD(lParam);\r
7253       pt.y = HIWORD(lParam);\r
7254       MenuPopup(hwnd, pt, hmenu, -1);\r
7255     }\r
7256     return 0;\r
7257   case WM_COMMAND:\r
7258     switch (LOWORD(wParam)) { \r
7259     case IDM_Undo:\r
7260       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7261       return 0;\r
7262     case IDM_SelectAll:\r
7263       sel.cpMin = 0;\r
7264       sel.cpMax = -1; /*999999?*/\r
7265       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7266       return 0;\r
7267     case IDM_Cut:\r
7268       SendMessage(hwnd, WM_CUT, 0, 0);\r
7269       return 0;\r
7270     case IDM_Paste:\r
7271       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7272       return 0;\r
7273     case IDM_Copy:\r
7274       SendMessage(hwnd, WM_COPY, 0, 0);\r
7275       return 0;\r
7276     }\r
7277     break;\r
7278   }\r
7279   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7280 }\r
7281 \r
7282 #define CO_MAX  100000\r
7283 #define CO_TRIM   1000\r
7284 \r
7285 LRESULT CALLBACK\r
7286 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7287 {\r
7288   static SnapData sd;\r
7289   static HWND hText, hInput, hFocus;\r
7290   InputSource *is = consoleInputSource;\r
7291   RECT rect;\r
7292   static int sizeX, sizeY;\r
7293   int newSizeX, newSizeY;\r
7294   MINMAXINFO *mmi;\r
7295 \r
7296   switch (message) {\r
7297   case WM_INITDIALOG: /* message: initialize dialog box */\r
7298     hwndConsole = hDlg;\r
7299     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7300     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7301     SetFocus(hInput);\r
7302     consoleTextWindowProc = (WNDPROC)\r
7303       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7304     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7305     consoleInputWindowProc = (WNDPROC)\r
7306       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7307     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7308     Colorize(ColorNormal, TRUE);\r
7309     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7310     ChangedConsoleFont();\r
7311     GetClientRect(hDlg, &rect);\r
7312     sizeX = rect.right;\r
7313     sizeY = rect.bottom;\r
7314     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7315         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7316       WINDOWPLACEMENT wp;\r
7317       EnsureOnScreen(&consoleX, &consoleY);\r
7318       wp.length = sizeof(WINDOWPLACEMENT);\r
7319       wp.flags = 0;\r
7320       wp.showCmd = SW_SHOW;\r
7321       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7322       wp.rcNormalPosition.left = consoleX;\r
7323       wp.rcNormalPosition.right = consoleX + consoleW;\r
7324       wp.rcNormalPosition.top = consoleY;\r
7325       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7326       SetWindowPlacement(hDlg, &wp);\r
7327     }\r
7328     return FALSE;\r
7329 \r
7330   case WM_SETFOCUS:\r
7331     SetFocus(hInput);\r
7332     return 0;\r
7333 \r
7334   case WM_CLOSE:\r
7335     ExitEvent(0);\r
7336     /* not reached */\r
7337     break;\r
7338 \r
7339   case WM_SIZE:\r
7340     if (IsIconic(hDlg)) break;\r
7341     newSizeX = LOWORD(lParam);\r
7342     newSizeY = HIWORD(lParam);\r
7343     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7344       RECT rectText, rectInput;\r
7345       POINT pt;\r
7346       int newTextHeight, newTextWidth;\r
7347       GetWindowRect(hText, &rectText);\r
7348       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7349       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7350       if (newTextHeight < 0) {\r
7351         newSizeY += -newTextHeight;\r
7352         newTextHeight = 0;\r
7353       }\r
7354       SetWindowPos(hText, NULL, 0, 0,\r
7355         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7356       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7357       pt.x = rectInput.left;\r
7358       pt.y = rectInput.top + newSizeY - sizeY;\r
7359       ScreenToClient(hDlg, &pt);\r
7360       SetWindowPos(hInput, NULL, \r
7361         pt.x, pt.y, /* needs client coords */   \r
7362         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7363         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7364     }\r
7365     sizeX = newSizeX;\r
7366     sizeY = newSizeY;\r
7367     break;\r
7368 \r
7369   case WM_GETMINMAXINFO:\r
7370     /* Prevent resizing window too small */\r
7371     mmi = (MINMAXINFO *) lParam;\r
7372     mmi->ptMinTrackSize.x = 100;\r
7373     mmi->ptMinTrackSize.y = 100;\r
7374     break;\r
7375 \r
7376   /* [AS] Snapping */\r
7377   case WM_ENTERSIZEMOVE:\r
7378     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7379 \r
7380   case WM_SIZING:\r
7381     return OnSizing( &sd, hDlg, wParam, lParam );\r
7382 \r
7383   case WM_MOVING:\r
7384     return OnMoving( &sd, hDlg, wParam, lParam );\r
7385 \r
7386   case WM_EXITSIZEMOVE:\r
7387     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7388   }\r
7389 \r
7390   return DefWindowProc(hDlg, message, wParam, lParam);\r
7391 }\r
7392 \r
7393 \r
7394 VOID\r
7395 ConsoleCreate()\r
7396 {\r
7397   HWND hCons;\r
7398   if (hwndConsole) return;\r
7399   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7400   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7401 }\r
7402 \r
7403 \r
7404 VOID\r
7405 ConsoleOutput(char* data, int length, int forceVisible)\r
7406 {\r
7407   HWND hText;\r
7408   int trim, exlen;\r
7409   char *p, *q;\r
7410   char buf[CO_MAX+1];\r
7411   POINT pEnd;\r
7412   RECT rect;\r
7413   static int delayLF = 0;\r
7414   CHARRANGE savesel, sel;\r
7415 \r
7416   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7417   p = data;\r
7418   q = buf;\r
7419   if (delayLF) {\r
7420     *q++ = '\r';\r
7421     *q++ = '\n';\r
7422     delayLF = 0;\r
7423   }\r
7424   while (length--) {\r
7425     if (*p == '\n') {\r
7426       if (*++p) {\r
7427         *q++ = '\r';\r
7428         *q++ = '\n';\r
7429       } else {\r
7430         delayLF = 1;\r
7431       }\r
7432     } else if (*p == '\007') {\r
7433        MyPlaySound(&sounds[(int)SoundBell]);\r
7434        p++;\r
7435     } else {\r
7436       *q++ = *p++;\r
7437     }\r
7438   }\r
7439   *q = NULLCHAR;\r
7440   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7441   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7442   /* Save current selection */\r
7443   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7444   exlen = GetWindowTextLength(hText);\r
7445   /* Find out whether current end of text is visible */\r
7446   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7447   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7448   /* Trim existing text if it's too long */\r
7449   if (exlen + (q - buf) > CO_MAX) {\r
7450     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7451     sel.cpMin = 0;\r
7452     sel.cpMax = trim;\r
7453     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7454     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7455     exlen -= trim;\r
7456     savesel.cpMin -= trim;\r
7457     savesel.cpMax -= trim;\r
7458     if (exlen < 0) exlen = 0;\r
7459     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7460     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7461   }\r
7462   /* Append the new text */\r
7463   sel.cpMin = exlen;\r
7464   sel.cpMax = exlen;\r
7465   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7466   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7467   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7468   if (forceVisible || exlen == 0 ||\r
7469       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7470        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7471     /* Scroll to make new end of text visible if old end of text\r
7472        was visible or new text is an echo of user typein */\r
7473     sel.cpMin = 9999999;\r
7474     sel.cpMax = 9999999;\r
7475     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7476     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7477     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7478     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7479   }\r
7480   if (savesel.cpMax == exlen || forceVisible) {\r
7481     /* Move insert point to new end of text if it was at the old\r
7482        end of text or if the new text is an echo of user typein */\r
7483     sel.cpMin = 9999999;\r
7484     sel.cpMax = 9999999;\r
7485     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7486   } else {\r
7487     /* Restore previous selection */\r
7488     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7489   }\r
7490   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7491 }\r
7492 \r
7493 /*---------*/\r
7494 \r
7495 \r
7496 void\r
7497 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7498 {\r
7499   char buf[100];\r
7500   char *str;\r
7501   COLORREF oldFg, oldBg;\r
7502   HFONT oldFont;\r
7503   RECT rect;\r
7504 \r
7505   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7506 \r
7507   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7508   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7509   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7510 \r
7511   rect.left = x;\r
7512   rect.right = x + squareSize;\r
7513   rect.top  = y;\r
7514   rect.bottom = y + squareSize;\r
7515   str = buf;\r
7516 \r
7517   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7518                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7519              y, ETO_CLIPPED|ETO_OPAQUE,\r
7520              &rect, str, strlen(str), NULL);\r
7521 \r
7522   (void) SetTextColor(hdc, oldFg);\r
7523   (void) SetBkColor(hdc, oldBg);\r
7524   (void) SelectObject(hdc, oldFont);\r
7525 }\r
7526 \r
7527 void\r
7528 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7529               RECT *rect, char *color, char *flagFell)\r
7530 {\r
7531   char buf[100];\r
7532   char *str;\r
7533   COLORREF oldFg, oldBg;\r
7534   HFONT oldFont;\r
7535 \r
7536   if (appData.clockMode) {\r
7537     if (tinyLayout)\r
7538       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7539     else\r
7540       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7541     str = buf;\r
7542   } else {\r
7543     str = color;\r
7544   }\r
7545 \r
7546   if (highlight) {\r
7547     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7548     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7549   } else {\r
7550     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7551     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7552   }\r
7553   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7554 \r
7555   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7556              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7557              rect, str, strlen(str), NULL);\r
7558 \r
7559   (void) SetTextColor(hdc, oldFg);\r
7560   (void) SetBkColor(hdc, oldBg);\r
7561   (void) SelectObject(hdc, oldFont);\r
7562 }\r
7563 \r
7564 \r
7565 int\r
7566 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7567            OVERLAPPED *ovl)\r
7568 {\r
7569   int ok, err;\r
7570 \r
7571   /* [AS]  */\r
7572   if( count <= 0 ) {\r
7573     if (appData.debugMode) {\r
7574       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7575     }\r
7576 \r
7577     return ERROR_INVALID_USER_BUFFER;\r
7578   }\r
7579 \r
7580   ResetEvent(ovl->hEvent);\r
7581   ovl->Offset = ovl->OffsetHigh = 0;\r
7582   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7583   if (ok) {\r
7584     err = NO_ERROR;\r
7585   } else {\r
7586     err = GetLastError();\r
7587     if (err == ERROR_IO_PENDING) {\r
7588       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7589       if (ok)\r
7590         err = NO_ERROR;\r
7591       else\r
7592         err = GetLastError();\r
7593     }\r
7594   }\r
7595   return err;\r
7596 }\r
7597 \r
7598 int\r
7599 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7600             OVERLAPPED *ovl)\r
7601 {\r
7602   int ok, err;\r
7603 \r
7604   ResetEvent(ovl->hEvent);\r
7605   ovl->Offset = ovl->OffsetHigh = 0;\r
7606   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7607   if (ok) {\r
7608     err = NO_ERROR;\r
7609   } else {\r
7610     err = GetLastError();\r
7611     if (err == ERROR_IO_PENDING) {\r
7612       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7613       if (ok)\r
7614         err = NO_ERROR;\r
7615       else\r
7616         err = GetLastError();\r
7617     }\r
7618   }\r
7619   return err;\r
7620 }\r
7621 \r
7622 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7623 void CheckForInputBufferFull( InputSource * is )\r
7624 {\r
7625     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7626         /* Look for end of line */\r
7627         char * p = is->buf;\r
7628         \r
7629         while( p < is->next && *p != '\n' ) {\r
7630             p++;\r
7631         }\r
7632 \r
7633         if( p >= is->next ) {\r
7634             if (appData.debugMode) {\r
7635                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7636             }\r
7637 \r
7638             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7639             is->count = (DWORD) -1;\r
7640             is->next = is->buf;\r
7641         }\r
7642     }\r
7643 }\r
7644 \r
7645 DWORD\r
7646 InputThread(LPVOID arg)\r
7647 {\r
7648   InputSource *is;\r
7649   OVERLAPPED ovl;\r
7650 \r
7651   is = (InputSource *) arg;\r
7652   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7653   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7654   while (is->hThread != NULL) {\r
7655     is->error = DoReadFile(is->hFile, is->next,\r
7656                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7657                            &is->count, &ovl);\r
7658     if (is->error == NO_ERROR) {\r
7659       is->next += is->count;\r
7660     } else {\r
7661       if (is->error == ERROR_BROKEN_PIPE) {\r
7662         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7663         is->count = 0;\r
7664       } else {\r
7665         is->count = (DWORD) -1;\r
7666         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7667         break; \r
7668       }\r
7669     }\r
7670 \r
7671     CheckForInputBufferFull( is );\r
7672 \r
7673     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7674 \r
7675     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7676 \r
7677     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7678   }\r
7679 \r
7680   CloseHandle(ovl.hEvent);\r
7681   CloseHandle(is->hFile);\r
7682 \r
7683   if (appData.debugMode) {\r
7684     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7685   }\r
7686 \r
7687   return 0;\r
7688 }\r
7689 \r
7690 \r
7691 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7692 DWORD\r
7693 NonOvlInputThread(LPVOID arg)\r
7694 {\r
7695   InputSource *is;\r
7696   char *p, *q;\r
7697   int i;\r
7698   char prev;\r
7699 \r
7700   is = (InputSource *) arg;\r
7701   while (is->hThread != NULL) {\r
7702     is->error = ReadFile(is->hFile, is->next,\r
7703                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7704                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7705     if (is->error == NO_ERROR) {\r
7706       /* Change CRLF to LF */\r
7707       if (is->next > is->buf) {\r
7708         p = is->next - 1;\r
7709         i = is->count + 1;\r
7710       } else {\r
7711         p = is->next;\r
7712         i = is->count;\r
7713       }\r
7714       q = p;\r
7715       prev = NULLCHAR;\r
7716       while (i > 0) {\r
7717         if (prev == '\r' && *p == '\n') {\r
7718           *(q-1) = '\n';\r
7719           is->count--;\r
7720         } else { \r
7721           *q++ = *p;\r
7722         }\r
7723         prev = *p++;\r
7724         i--;\r
7725       }\r
7726       *q = NULLCHAR;\r
7727       is->next = q;\r
7728     } else {\r
7729       if (is->error == ERROR_BROKEN_PIPE) {\r
7730         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7731         is->count = 0; \r
7732       } else {\r
7733         is->count = (DWORD) -1;\r
7734       }\r
7735     }\r
7736 \r
7737     CheckForInputBufferFull( is );\r
7738 \r
7739     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7740 \r
7741     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7742 \r
7743     if (is->count < 0) break;  /* Quit on error */\r
7744   }\r
7745   CloseHandle(is->hFile);\r
7746   return 0;\r
7747 }\r
7748 \r
7749 DWORD\r
7750 SocketInputThread(LPVOID arg)\r
7751 {\r
7752   InputSource *is;\r
7753 \r
7754   is = (InputSource *) arg;\r
7755   while (is->hThread != NULL) {\r
7756     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7757     if ((int)is->count == SOCKET_ERROR) {\r
7758       is->count = (DWORD) -1;\r
7759       is->error = WSAGetLastError();\r
7760     } else {\r
7761       is->error = NO_ERROR;\r
7762       is->next += is->count;\r
7763       if (is->count == 0 && is->second == is) {\r
7764         /* End of file on stderr; quit with no message */\r
7765         break;\r
7766       }\r
7767     }\r
7768     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7769 \r
7770     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7771 \r
7772     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7773   }\r
7774   return 0;\r
7775 }\r
7776 \r
7777 VOID\r
7778 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7779 {\r
7780   InputSource *is;\r
7781 \r
7782   is = (InputSource *) lParam;\r
7783   if (is->lineByLine) {\r
7784     /* Feed in lines one by one */\r
7785     char *p = is->buf;\r
7786     char *q = p;\r
7787     while (q < is->next) {\r
7788       if (*q++ == '\n') {\r
7789         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7790         p = q;\r
7791       }\r
7792     }\r
7793     \r
7794     /* Move any partial line to the start of the buffer */\r
7795     q = is->buf;\r
7796     while (p < is->next) {\r
7797       *q++ = *p++;\r
7798     }\r
7799     is->next = q;\r
7800 \r
7801     if (is->error != NO_ERROR || is->count == 0) {\r
7802       /* Notify backend of the error.  Note: If there was a partial\r
7803          line at the end, it is not flushed through. */\r
7804       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7805     }\r
7806   } else {\r
7807     /* Feed in the whole chunk of input at once */\r
7808     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7809     is->next = is->buf;\r
7810   }\r
7811 }\r
7812 \r
7813 /*---------------------------------------------------------------------------*\\r
7814  *\r
7815  *  Menu enables. Used when setting various modes.\r
7816  *\r
7817 \*---------------------------------------------------------------------------*/\r
7818 \r
7819 typedef struct {\r
7820   int item;\r
7821   int flags;\r
7822 } Enables;\r
7823 \r
7824 VOID\r
7825 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7826 {\r
7827   while (enab->item > 0) {\r
7828     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7829     enab++;\r
7830   }\r
7831 }\r
7832 \r
7833 Enables gnuEnables[] = {\r
7834   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables icsEnables[] = {\r
7848   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7860   { -1, -1 }\r
7861 };\r
7862 \r
7863 #ifdef ZIPPY\r
7864 Enables zippyEnables[] = {\r
7865   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7868   { -1, -1 }\r
7869 };\r
7870 #endif\r
7871 \r
7872 Enables ncpEnables[] = {\r
7873   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7881   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7882   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7888   { -1, -1 }\r
7889 };\r
7890 \r
7891 Enables trainingOnEnables[] = {\r
7892   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7900   { -1, -1 }\r
7901 };\r
7902 \r
7903 Enables trainingOffEnables[] = {\r
7904   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7912   { -1, -1 }\r
7913 };\r
7914 \r
7915 /* These modify either ncpEnables or gnuEnables */\r
7916 Enables cmailEnables[] = {\r
7917   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7919   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7920   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7924   { -1, -1 }\r
7925 };\r
7926 \r
7927 Enables machineThinkingEnables[] = {\r
7928   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7943   { -1, -1 }\r
7944 };\r
7945 \r
7946 Enables userThinkingEnables[] = {\r
7947   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7961   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7962   { -1, -1 }\r
7963 };\r
7964 \r
7965 /*---------------------------------------------------------------------------*\\r
7966  *\r
7967  *  Front-end interface functions exported by XBoard.\r
7968  *  Functions appear in same order as prototypes in frontend.h.\r
7969  * \r
7970 \*---------------------------------------------------------------------------*/\r
7971 VOID\r
7972 ModeHighlight()\r
7973 {\r
7974   static UINT prevChecked = 0;\r
7975   static int prevPausing = 0;\r
7976   UINT nowChecked;\r
7977 \r
7978   if (pausing != prevPausing) {\r
7979     prevPausing = pausing;\r
7980     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7981                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7982     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7983   }\r
7984 \r
7985   switch (gameMode) {\r
7986   case BeginningOfGame:\r
7987     if (appData.icsActive)\r
7988       nowChecked = IDM_IcsClient;\r
7989     else if (appData.noChessProgram)\r
7990       nowChecked = IDM_EditGame;\r
7991     else\r
7992       nowChecked = IDM_MachineBlack;\r
7993     break;\r
7994   case MachinePlaysBlack:\r
7995     nowChecked = IDM_MachineBlack;\r
7996     break;\r
7997   case MachinePlaysWhite:\r
7998     nowChecked = IDM_MachineWhite;\r
7999     break;\r
8000   case TwoMachinesPlay:\r
8001     nowChecked = IDM_TwoMachines;\r
8002     break;\r
8003   case AnalyzeMode:\r
8004     nowChecked = IDM_AnalysisMode;\r
8005     break;\r
8006   case AnalyzeFile:\r
8007     nowChecked = IDM_AnalyzeFile;\r
8008     break;\r
8009   case EditGame:\r
8010     nowChecked = IDM_EditGame;\r
8011     break;\r
8012   case PlayFromGameFile:\r
8013     nowChecked = IDM_LoadGame;\r
8014     break;\r
8015   case EditPosition:\r
8016     nowChecked = IDM_EditPosition;\r
8017     break;\r
8018   case Training:\r
8019     nowChecked = IDM_Training;\r
8020     break;\r
8021   case IcsPlayingWhite:\r
8022   case IcsPlayingBlack:\r
8023   case IcsObserving:\r
8024   case IcsIdle:\r
8025     nowChecked = IDM_IcsClient;\r
8026     break;\r
8027   default:\r
8028   case EndOfGame:\r
8029     nowChecked = 0;\r
8030     break;\r
8031   }\r
8032   if (prevChecked != 0)\r
8033     (void) CheckMenuItem(GetMenu(hwndMain),\r
8034                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8035   if (nowChecked != 0)\r
8036     (void) CheckMenuItem(GetMenu(hwndMain),\r
8037                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8038 \r
8039   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8040     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8041                           MF_BYCOMMAND|MF_ENABLED);\r
8042   } else {\r
8043     (void) EnableMenuItem(GetMenu(hwndMain), \r
8044                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8045   }\r
8046 \r
8047   prevChecked = nowChecked;\r
8048 }\r
8049 \r
8050 VOID\r
8051 SetICSMode()\r
8052 {\r
8053   HMENU hmenu = GetMenu(hwndMain);\r
8054   SetMenuEnables(hmenu, icsEnables);\r
8055   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8056     MF_BYPOSITION|MF_ENABLED);\r
8057 #ifdef ZIPPY\r
8058   if (appData.zippyPlay) {\r
8059     SetMenuEnables(hmenu, zippyEnables);\r
8060   }\r
8061 #endif\r
8062 }\r
8063 \r
8064 VOID\r
8065 SetGNUMode()\r
8066 {\r
8067   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8068 }\r
8069 \r
8070 VOID\r
8071 SetNCPMode()\r
8072 {\r
8073   HMENU hmenu = GetMenu(hwndMain);\r
8074   SetMenuEnables(hmenu, ncpEnables);\r
8075   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8076     MF_BYPOSITION|MF_GRAYED);\r
8077     DrawMenuBar(hwndMain);\r
8078 }\r
8079 \r
8080 VOID\r
8081 SetCmailMode()\r
8082 {\r
8083   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8084 }\r
8085 \r
8086 VOID \r
8087 SetTrainingModeOn()\r
8088 {\r
8089   int i;\r
8090   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8091   for (i = 0; i < N_BUTTONS; i++) {\r
8092     if (buttonDesc[i].hwnd != NULL)\r
8093       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8094   }\r
8095   CommentPopDown();\r
8096 }\r
8097 \r
8098 VOID SetTrainingModeOff()\r
8099 {\r
8100   int i;\r
8101   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8102   for (i = 0; i < N_BUTTONS; i++) {\r
8103     if (buttonDesc[i].hwnd != NULL)\r
8104       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8105   }\r
8106 }\r
8107 \r
8108 \r
8109 VOID\r
8110 SetUserThinkingEnables()\r
8111 {\r
8112   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8113 }\r
8114 \r
8115 VOID\r
8116 SetMachineThinkingEnables()\r
8117 {\r
8118   HMENU hMenu = GetMenu(hwndMain);\r
8119   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8120 \r
8121   SetMenuEnables(hMenu, machineThinkingEnables);\r
8122 \r
8123   if (gameMode == MachinePlaysBlack) {\r
8124     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8125   } else if (gameMode == MachinePlaysWhite) {\r
8126     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8127   } else if (gameMode == TwoMachinesPlay) {\r
8128     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8129   }\r
8130 }\r
8131 \r
8132 \r
8133 VOID\r
8134 DisplayTitle(char *str)\r
8135 {\r
8136   char title[MSG_SIZ], *host;\r
8137   if (str[0] != NULLCHAR) {\r
8138     strcpy(title, str);\r
8139   } else if (appData.icsActive) {\r
8140     if (appData.icsCommPort[0] != NULLCHAR)\r
8141       host = "ICS";\r
8142     else \r
8143       host = appData.icsHost;\r
8144     sprintf(title, "%s: %s", szTitle, host);\r
8145   } else if (appData.noChessProgram) {\r
8146     strcpy(title, szTitle);\r
8147   } else {\r
8148     strcpy(title, szTitle);\r
8149     strcat(title, ": ");\r
8150     strcat(title, first.tidy);\r
8151   }\r
8152   SetWindowText(hwndMain, title);\r
8153 }\r
8154 \r
8155 \r
8156 VOID\r
8157 DisplayMessage(char *str1, char *str2)\r
8158 {\r
8159   HDC hdc;\r
8160   HFONT oldFont;\r
8161   int remain = MESSAGE_TEXT_MAX - 1;\r
8162   int len;\r
8163 \r
8164   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8165   messageText[0] = NULLCHAR;\r
8166   if (*str1) {\r
8167     len = strlen(str1);\r
8168     if (len > remain) len = remain;\r
8169     strncpy(messageText, str1, len);\r
8170     messageText[len] = NULLCHAR;\r
8171     remain -= len;\r
8172   }\r
8173   if (*str2 && remain >= 2) {\r
8174     if (*str1) {\r
8175       strcat(messageText, "  ");\r
8176       remain -= 2;\r
8177     }\r
8178     len = strlen(str2);\r
8179     if (len > remain) len = remain;\r
8180     strncat(messageText, str2, len);\r
8181   }\r
8182   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8183 \r
8184   if (IsIconic(hwndMain)) return;\r
8185   hdc = GetDC(hwndMain);\r
8186   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8187   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8188              &messageRect, messageText, strlen(messageText), NULL);\r
8189   (void) SelectObject(hdc, oldFont);\r
8190   (void) ReleaseDC(hwndMain, hdc);\r
8191 }\r
8192 \r
8193 VOID\r
8194 DisplayError(char *str, int error)\r
8195 {\r
8196   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8197   int len;\r
8198 \r
8199   if (error == 0) {\r
8200     strcpy(buf, str);\r
8201   } else {\r
8202     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8203                         NULL, error, LANG_NEUTRAL,\r
8204                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8205     if (len > 0) {\r
8206       sprintf(buf, "%s:\n%s", str, buf2);\r
8207     } else {\r
8208       ErrorMap *em = errmap;\r
8209       while (em->err != 0 && em->err != error) em++;\r
8210       if (em->err != 0) {\r
8211         sprintf(buf, "%s:\n%s", str, em->msg);\r
8212       } else {\r
8213         sprintf(buf, "%s:\nError code %d", str, error);\r
8214       }\r
8215     }\r
8216   }\r
8217   \r
8218   ErrorPopUp("Error", buf);\r
8219 }\r
8220 \r
8221 \r
8222 VOID\r
8223 DisplayMoveError(char *str)\r
8224 {\r
8225   fromX = fromY = -1;\r
8226   ClearHighlights();\r
8227   DrawPosition(FALSE, NULL);\r
8228   if (appData.popupMoveErrors) {\r
8229     ErrorPopUp("Error", str);\r
8230   } else {\r
8231     DisplayMessage(str, "");\r
8232     moveErrorMessageUp = TRUE;\r
8233   }\r
8234 }\r
8235 \r
8236 VOID\r
8237 DisplayFatalError(char *str, int error, int exitStatus)\r
8238 {\r
8239   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8240   int len;\r
8241   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8242 \r
8243   if (error != 0) {\r
8244     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8245                         NULL, error, LANG_NEUTRAL,\r
8246                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8247     if (len > 0) {\r
8248       sprintf(buf, "%s:\n%s", str, buf2);\r
8249     } else {\r
8250       ErrorMap *em = errmap;\r
8251       while (em->err != 0 && em->err != error) em++;\r
8252       if (em->err != 0) {\r
8253         sprintf(buf, "%s:\n%s", str, em->msg);\r
8254       } else {\r
8255         sprintf(buf, "%s:\nError code %d", str, error);\r
8256       }\r
8257     }\r
8258     str = buf;\r
8259   }\r
8260   if (appData.debugMode) {\r
8261     fprintf(debugFP, "%s: %s\n", label, str);\r
8262   }\r
8263   if (appData.popupExitMessage) {\r
8264     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8265                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8266   }\r
8267   ExitEvent(exitStatus);\r
8268 }\r
8269 \r
8270 \r
8271 VOID\r
8272 DisplayInformation(char *str)\r
8273 {\r
8274   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8275 }\r
8276 \r
8277 \r
8278 VOID\r
8279 DisplayNote(char *str)\r
8280 {\r
8281   ErrorPopUp("Note", str);\r
8282 }\r
8283 \r
8284 \r
8285 typedef struct {\r
8286   char *title, *question, *replyPrefix;\r
8287   ProcRef pr;\r
8288 } QuestionParams;\r
8289 \r
8290 LRESULT CALLBACK\r
8291 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8292 {\r
8293   static QuestionParams *qp;\r
8294   char reply[MSG_SIZ];\r
8295   int len, err;\r
8296 \r
8297   switch (message) {\r
8298   case WM_INITDIALOG:\r
8299     qp = (QuestionParams *) lParam;\r
8300     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8301     SetWindowText(hDlg, qp->title);\r
8302     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8303     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8304     return FALSE;\r
8305 \r
8306   case WM_COMMAND:\r
8307     switch (LOWORD(wParam)) {\r
8308     case IDOK:\r
8309       strcpy(reply, qp->replyPrefix);\r
8310       if (*reply) strcat(reply, " ");\r
8311       len = strlen(reply);\r
8312       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8313       strcat(reply, "\n");\r
8314       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8315       EndDialog(hDlg, TRUE);\r
8316       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8317       return TRUE;\r
8318     case IDCANCEL:\r
8319       EndDialog(hDlg, FALSE);\r
8320       return TRUE;\r
8321     default:\r
8322       break;\r
8323     }\r
8324     break;\r
8325   }\r
8326   return FALSE;\r
8327 }\r
8328 \r
8329 VOID\r
8330 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8331 {\r
8332     QuestionParams qp;\r
8333     FARPROC lpProc;\r
8334     \r
8335     qp.title = title;\r
8336     qp.question = question;\r
8337     qp.replyPrefix = replyPrefix;\r
8338     qp.pr = pr;\r
8339     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8340     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8341       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8342     FreeProcInstance(lpProc);\r
8343 }\r
8344 \r
8345 /* [AS] Pick FRC position */\r
8346 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8347 {\r
8348     static int * lpIndexFRC;\r
8349     BOOL index_is_ok;\r
8350     char buf[16];\r
8351 \r
8352     switch( message )\r
8353     {\r
8354     case WM_INITDIALOG:\r
8355         lpIndexFRC = (int *) lParam;\r
8356 \r
8357         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8358 \r
8359         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8360         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8361         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8362         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8363 \r
8364         break;\r
8365 \r
8366     case WM_COMMAND:\r
8367         switch( LOWORD(wParam) ) {\r
8368         case IDOK:\r
8369             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8370             EndDialog( hDlg, 0 );\r
8371             return TRUE;\r
8372         case IDCANCEL:\r
8373             EndDialog( hDlg, 1 );   \r
8374             return TRUE;\r
8375         case IDC_NFG_Edit:\r
8376             if( HIWORD(wParam) == EN_CHANGE ) {\r
8377                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8378 \r
8379                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8380             }\r
8381             return TRUE;\r
8382         case IDC_NFG_Random:\r
8383             sprintf( buf, "%d", myrandom() % 960 );\r
8384             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8385             return TRUE;\r
8386         }\r
8387 \r
8388         break;\r
8389     }\r
8390 \r
8391     return FALSE;\r
8392 }\r
8393 \r
8394 int NewGameFRC()\r
8395 {\r
8396     int result;\r
8397     int index = appData.defaultFrcPosition;\r
8398     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8399 \r
8400     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8401 \r
8402     if( result == 0 ) {\r
8403         appData.defaultFrcPosition = index;\r
8404     }\r
8405 \r
8406     return result;\r
8407 }\r
8408 \r
8409 /* [AS] Game list options */\r
8410 typedef struct {\r
8411     char id;\r
8412     char * name;\r
8413 } GLT_Item;\r
8414 \r
8415 static GLT_Item GLT_ItemInfo[] = {\r
8416     { GLT_EVENT,      "Event" },\r
8417     { GLT_SITE,       "Site" },\r
8418     { GLT_DATE,       "Date" },\r
8419     { GLT_ROUND,      "Round" },\r
8420     { GLT_PLAYERS,    "Players" },\r
8421     { GLT_RESULT,     "Result" },\r
8422     { GLT_WHITE_ELO,  "White Rating" },\r
8423     { GLT_BLACK_ELO,  "Black Rating" },\r
8424     { GLT_TIME_CONTROL,"Time Control" },\r
8425     { GLT_VARIANT,    "Variant" },\r
8426     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8427     { 0, 0 }\r
8428 };\r
8429 \r
8430 const char * GLT_FindItem( char id )\r
8431 {\r
8432     const char * result = 0;\r
8433 \r
8434     GLT_Item * list = GLT_ItemInfo;\r
8435 \r
8436     while( list->id != 0 ) {\r
8437         if( list->id == id ) {\r
8438             result = list->name;\r
8439             break;\r
8440         }\r
8441 \r
8442         list++;\r
8443     }\r
8444 \r
8445     return result;\r
8446 }\r
8447 \r
8448 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8449 {\r
8450     const char * name = GLT_FindItem( id );\r
8451 \r
8452     if( name != 0 ) {\r
8453         if( index >= 0 ) {\r
8454             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8455         }\r
8456         else {\r
8457             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8458         }\r
8459     }\r
8460 }\r
8461 \r
8462 void GLT_TagsToList( HWND hDlg, char * tags )\r
8463 {\r
8464     char * pc = tags;\r
8465 \r
8466     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8467 \r
8468     while( *pc ) {\r
8469         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8470         pc++;\r
8471     }\r
8472 \r
8473     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8474 \r
8475     pc = GLT_ALL_TAGS;\r
8476 \r
8477     while( *pc ) {\r
8478         if( strchr( tags, *pc ) == 0 ) {\r
8479             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8480         }\r
8481         pc++;\r
8482     }\r
8483 \r
8484     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8485 }\r
8486 \r
8487 char GLT_ListItemToTag( HWND hDlg, int index )\r
8488 {\r
8489     char result = '\0';\r
8490     char name[128];\r
8491 \r
8492     GLT_Item * list = GLT_ItemInfo;\r
8493 \r
8494     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8495         while( list->id != 0 ) {\r
8496             if( strcmp( list->name, name ) == 0 ) {\r
8497                 result = list->id;\r
8498                 break;\r
8499             }\r
8500 \r
8501             list++;\r
8502         }\r
8503     }\r
8504 \r
8505     return result;\r
8506 }\r
8507 \r
8508 void GLT_MoveSelection( HWND hDlg, int delta )\r
8509 {\r
8510     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8511     int idx2 = idx1 + delta;\r
8512     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8513 \r
8514     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8515         char buf[128];\r
8516 \r
8517         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8518         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8519         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8520         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8521     }\r
8522 }\r
8523 \r
8524 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8525 {\r
8526     static char glt[64];\r
8527     static char * lpUserGLT;\r
8528 \r
8529     switch( message )\r
8530     {\r
8531     case WM_INITDIALOG:\r
8532         lpUserGLT = (char *) lParam;\r
8533         \r
8534         strcpy( glt, lpUserGLT );\r
8535 \r
8536         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8537 \r
8538         /* Initialize list */\r
8539         GLT_TagsToList( hDlg, glt );\r
8540 \r
8541         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8542 \r
8543         break;\r
8544 \r
8545     case WM_COMMAND:\r
8546         switch( LOWORD(wParam) ) {\r
8547         case IDOK:\r
8548             {\r
8549                 char * pc = lpUserGLT;\r
8550                 int idx = 0;\r
8551                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8552                 char id;\r
8553 \r
8554                 do {\r
8555                     id = GLT_ListItemToTag( hDlg, idx );\r
8556 \r
8557                     *pc++ = id;\r
8558                     idx++;\r
8559                 } while( id != '\0' );\r
8560             }\r
8561             EndDialog( hDlg, 0 );\r
8562             return TRUE;\r
8563         case IDCANCEL:\r
8564             EndDialog( hDlg, 1 );\r
8565             return TRUE;\r
8566 \r
8567         case IDC_GLT_Default:\r
8568             strcpy( glt, GLT_DEFAULT_TAGS );\r
8569             GLT_TagsToList( hDlg, glt );\r
8570             return TRUE;\r
8571 \r
8572         case IDC_GLT_Restore:\r
8573             strcpy( glt, lpUserGLT );\r
8574             GLT_TagsToList( hDlg, glt );\r
8575             return TRUE;\r
8576 \r
8577         case IDC_GLT_Up:\r
8578             GLT_MoveSelection( hDlg, -1 );\r
8579             return TRUE;\r
8580 \r
8581         case IDC_GLT_Down:\r
8582             GLT_MoveSelection( hDlg, +1 );\r
8583             return TRUE;\r
8584         }\r
8585 \r
8586         break;\r
8587     }\r
8588 \r
8589     return FALSE;\r
8590 }\r
8591 \r
8592 int GameListOptions()\r
8593 {\r
8594     char glt[64];\r
8595     int result;\r
8596     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8597 \r
8598     strcpy( glt, appData.gameListTags );\r
8599 \r
8600     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8601 \r
8602     if( result == 0 ) {\r
8603         /* [AS] Memory leak here! */\r
8604         appData.gameListTags = strdup( glt ); \r
8605     }\r
8606 \r
8607     return result;\r
8608 }\r
8609 \r
8610 \r
8611 VOID\r
8612 DisplayIcsInteractionTitle(char *str)\r
8613 {\r
8614   char consoleTitle[MSG_SIZ];\r
8615 \r
8616   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8617   SetWindowText(hwndConsole, consoleTitle);\r
8618 }\r
8619 \r
8620 void\r
8621 DrawPosition(int fullRedraw, Board board)\r
8622 {\r
8623   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8624 }\r
8625 \r
8626 \r
8627 VOID\r
8628 ResetFrontEnd()\r
8629 {\r
8630   fromX = fromY = -1;\r
8631   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8632     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8633     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8634     dragInfo.lastpos = dragInfo.pos;\r
8635     dragInfo.start.x = dragInfo.start.y = -1;\r
8636     dragInfo.from = dragInfo.start;\r
8637     ReleaseCapture();\r
8638     DrawPosition(TRUE, NULL);\r
8639   }\r
8640 }\r
8641 \r
8642 \r
8643 VOID\r
8644 CommentPopUp(char *title, char *str)\r
8645 {\r
8646   HWND hwnd = GetActiveWindow();\r
8647   EitherCommentPopUp(0, title, str, FALSE);\r
8648   SetActiveWindow(hwnd);\r
8649 }\r
8650 \r
8651 VOID\r
8652 CommentPopDown(void)\r
8653 {\r
8654   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8655   if (commentDialog) {\r
8656     ShowWindow(commentDialog, SW_HIDE);\r
8657   }\r
8658   commentDialogUp = FALSE;\r
8659 }\r
8660 \r
8661 VOID\r
8662 EditCommentPopUp(int index, char *title, char *str)\r
8663 {\r
8664   EitherCommentPopUp(index, title, str, TRUE);\r
8665 }\r
8666 \r
8667 \r
8668 VOID\r
8669 RingBell()\r
8670 {\r
8671   MyPlaySound(&sounds[(int)SoundMove]);\r
8672 }\r
8673 \r
8674 VOID PlayIcsWinSound()\r
8675 {\r
8676   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8677 }\r
8678 \r
8679 VOID PlayIcsLossSound()\r
8680 {\r
8681   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8682 }\r
8683 \r
8684 VOID PlayIcsDrawSound()\r
8685 {\r
8686   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8687 }\r
8688 \r
8689 VOID PlayIcsUnfinishedSound()\r
8690 {\r
8691   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8692 }\r
8693 \r
8694 VOID\r
8695 PlayAlarmSound()\r
8696 {\r
8697   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8698 }\r
8699 \r
8700 \r
8701 VOID\r
8702 EchoOn()\r
8703 {\r
8704   HWND hInput;\r
8705   consoleEcho = TRUE;\r
8706   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8707   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8708   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8709 }\r
8710 \r
8711 \r
8712 VOID\r
8713 EchoOff()\r
8714 {\r
8715   CHARFORMAT cf;\r
8716   HWND hInput;\r
8717   consoleEcho = FALSE;\r
8718   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8719   /* This works OK: set text and background both to the same color */\r
8720   cf = consoleCF;\r
8721   cf.crTextColor = COLOR_ECHOOFF;\r
8722   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8723   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8724 }\r
8725 \r
8726 /* No Raw()...? */\r
8727 \r
8728 void Colorize(ColorClass cc, int continuation)\r
8729 {\r
8730   currentColorClass = cc;\r
8731   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8732   consoleCF.crTextColor = textAttribs[cc].color;\r
8733   consoleCF.dwEffects = textAttribs[cc].effects;\r
8734   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8735 }\r
8736 \r
8737 char *\r
8738 UserName()\r
8739 {\r
8740   static char buf[MSG_SIZ];\r
8741   DWORD bufsiz = MSG_SIZ;\r
8742 \r
8743   if (!GetUserName(buf, &bufsiz)) {\r
8744     /*DisplayError("Error getting user name", GetLastError());*/\r
8745     strcpy(buf, "User");\r
8746   }\r
8747   return buf;\r
8748 }\r
8749 \r
8750 char *\r
8751 HostName()\r
8752 {\r
8753   static char buf[MSG_SIZ];\r
8754   DWORD bufsiz = MSG_SIZ;\r
8755 \r
8756   if (!GetComputerName(buf, &bufsiz)) {\r
8757     /*DisplayError("Error getting host name", GetLastError());*/\r
8758     strcpy(buf, "Unknown");\r
8759   }\r
8760   return buf;\r
8761 }\r
8762 \r
8763 \r
8764 int\r
8765 ClockTimerRunning()\r
8766 {\r
8767   return clockTimerEvent != 0;\r
8768 }\r
8769 \r
8770 int\r
8771 StopClockTimer()\r
8772 {\r
8773   if (clockTimerEvent == 0) return FALSE;\r
8774   KillTimer(hwndMain, clockTimerEvent);\r
8775   clockTimerEvent = 0;\r
8776   return TRUE;\r
8777 }\r
8778 \r
8779 void\r
8780 StartClockTimer(long millisec)\r
8781 {\r
8782   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8783                              (UINT) millisec, NULL);\r
8784 }\r
8785 \r
8786 void\r
8787 DisplayWhiteClock(long timeRemaining, int highlight)\r
8788 {\r
8789   HDC hdc;\r
8790   hdc = GetDC(hwndMain);\r
8791   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8792 \r
8793   if (!IsIconic(hwndMain)) {\r
8794     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
8795   }\r
8796   if (highlight && iconCurrent == iconBlack) {\r
8797     iconCurrent = iconWhite;\r
8798     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8799     if (IsIconic(hwndMain)) {\r
8800       DrawIcon(hdc, 2, 2, iconCurrent);\r
8801     }\r
8802   }\r
8803   (void) ReleaseDC(hwndMain, hdc);\r
8804   if (hwndConsole)\r
8805     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8806 }\r
8807 \r
8808 void\r
8809 DisplayBlackClock(long timeRemaining, int highlight)\r
8810 {\r
8811   HDC hdc;\r
8812   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8813 \r
8814   hdc = GetDC(hwndMain);\r
8815   if (!IsIconic(hwndMain)) {\r
8816     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8817   }\r
8818   if (highlight && iconCurrent == iconWhite) {\r
8819     iconCurrent = iconBlack;\r
8820     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8821     if (IsIconic(hwndMain)) {\r
8822       DrawIcon(hdc, 2, 2, iconCurrent);\r
8823     }\r
8824   }\r
8825   (void) ReleaseDC(hwndMain, hdc);\r
8826   if (hwndConsole)\r
8827     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8828 }\r
8829 \r
8830 \r
8831 int\r
8832 LoadGameTimerRunning()\r
8833 {\r
8834   return loadGameTimerEvent != 0;\r
8835 }\r
8836 \r
8837 int\r
8838 StopLoadGameTimer()\r
8839 {\r
8840   if (loadGameTimerEvent == 0) return FALSE;\r
8841   KillTimer(hwndMain, loadGameTimerEvent);\r
8842   loadGameTimerEvent = 0;\r
8843   return TRUE;\r
8844 }\r
8845 \r
8846 void\r
8847 StartLoadGameTimer(long millisec)\r
8848 {\r
8849   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8850                                 (UINT) millisec, NULL);\r
8851 }\r
8852 \r
8853 void\r
8854 AutoSaveGame()\r
8855 {\r
8856   char *defName;\r
8857   FILE *f;\r
8858   char fileTitle[MSG_SIZ];\r
8859 \r
8860   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8861   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8862                      appData.oldSaveStyle ? "gam" : "pgn",\r
8863                      GAME_FILT, \r
8864                      "Save Game to File", NULL, fileTitle, NULL);\r
8865   if (f != NULL) {\r
8866     SaveGame(f, 0, "");\r
8867     fclose(f);\r
8868   }\r
8869 }\r
8870 \r
8871 \r
8872 void\r
8873 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8874 {\r
8875   if (delayedTimerEvent != 0) {\r
8876     if (appData.debugMode) {\r
8877       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8878     }\r
8879     KillTimer(hwndMain, delayedTimerEvent);\r
8880     delayedTimerEvent = 0;\r
8881     delayedTimerCallback();\r
8882   }\r
8883   delayedTimerCallback = cb;\r
8884   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8885                                 (UINT) millisec, NULL);\r
8886 }\r
8887 \r
8888 DelayedEventCallback\r
8889 GetDelayedEvent()\r
8890 {\r
8891   if (delayedTimerEvent) {\r
8892     return delayedTimerCallback;\r
8893   } else {\r
8894     return NULL;\r
8895   }\r
8896 }\r
8897 \r
8898 void\r
8899 CancelDelayedEvent()\r
8900 {\r
8901   if (delayedTimerEvent) {\r
8902     KillTimer(hwndMain, delayedTimerEvent);\r
8903     delayedTimerEvent = 0;\r
8904   }\r
8905 }\r
8906 \r
8907 /* Start a child process running the given program.\r
8908    The process's standard output can be read from "from", and its\r
8909    standard input can be written to "to".\r
8910    Exit with fatal error if anything goes wrong.\r
8911    Returns an opaque pointer that can be used to destroy the process\r
8912    later.\r
8913 */\r
8914 int\r
8915 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8916 {\r
8917 #define BUFSIZE 4096\r
8918 \r
8919   HANDLE hChildStdinRd, hChildStdinWr,\r
8920     hChildStdoutRd, hChildStdoutWr;\r
8921   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8922   SECURITY_ATTRIBUTES saAttr;\r
8923   BOOL fSuccess;\r
8924   PROCESS_INFORMATION piProcInfo;\r
8925   STARTUPINFO siStartInfo;\r
8926   ChildProc *cp;\r
8927   char buf[MSG_SIZ];\r
8928   DWORD err;\r
8929 \r
8930   if (appData.debugMode) {\r
8931     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8932   }\r
8933 \r
8934   *pr = NoProc;\r
8935 \r
8936   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8937   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8938   saAttr.bInheritHandle = TRUE;\r
8939   saAttr.lpSecurityDescriptor = NULL;\r
8940 \r
8941   /*\r
8942    * The steps for redirecting child's STDOUT:\r
8943    *     1. Create anonymous pipe to be STDOUT for child.\r
8944    *     2. Create a noninheritable duplicate of read handle,\r
8945    *         and close the inheritable read handle.\r
8946    */\r
8947 \r
8948   /* Create a pipe for the child's STDOUT. */\r
8949   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8950     return GetLastError();\r
8951   }\r
8952 \r
8953   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8954   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8955                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8956                              FALSE,     /* not inherited */\r
8957                              DUPLICATE_SAME_ACCESS);\r
8958   if (! fSuccess) {\r
8959     return GetLastError();\r
8960   }\r
8961   CloseHandle(hChildStdoutRd);\r
8962 \r
8963   /*\r
8964    * The steps for redirecting child's STDIN:\r
8965    *     1. Create anonymous pipe to be STDIN for child.\r
8966    *     2. Create a noninheritable duplicate of write handle,\r
8967    *         and close the inheritable write handle.\r
8968    */\r
8969 \r
8970   /* Create a pipe for the child's STDIN. */\r
8971   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8972     return GetLastError();\r
8973   }\r
8974 \r
8975   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8976   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8977                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8978                              FALSE,     /* not inherited */\r
8979                              DUPLICATE_SAME_ACCESS);\r
8980   if (! fSuccess) {\r
8981     return GetLastError();\r
8982   }\r
8983   CloseHandle(hChildStdinWr);\r
8984 \r
8985   /* Arrange to (1) look in dir for the child .exe file, and\r
8986    * (2) have dir be the child's working directory.  Interpret\r
8987    * dir relative to the directory WinBoard loaded from. */\r
8988   GetCurrentDirectory(MSG_SIZ, buf);\r
8989   SetCurrentDirectory(installDir);\r
8990   SetCurrentDirectory(dir);\r
8991 \r
8992   /* Now create the child process. */\r
8993 \r
8994   siStartInfo.cb = sizeof(STARTUPINFO);\r
8995   siStartInfo.lpReserved = NULL;\r
8996   siStartInfo.lpDesktop = NULL;\r
8997   siStartInfo.lpTitle = NULL;\r
8998   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8999   siStartInfo.cbReserved2 = 0;\r
9000   siStartInfo.lpReserved2 = NULL;\r
9001   siStartInfo.hStdInput = hChildStdinRd;\r
9002   siStartInfo.hStdOutput = hChildStdoutWr;\r
9003   siStartInfo.hStdError = hChildStdoutWr;\r
9004 \r
9005   fSuccess = CreateProcess(NULL,\r
9006                            cmdLine,        /* command line */\r
9007                            NULL,           /* process security attributes */\r
9008                            NULL,           /* primary thread security attrs */\r
9009                            TRUE,           /* handles are inherited */\r
9010                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9011                            NULL,           /* use parent's environment */\r
9012                            NULL,\r
9013                            &siStartInfo, /* STARTUPINFO pointer */\r
9014                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9015 \r
9016   err = GetLastError();\r
9017   SetCurrentDirectory(buf); /* return to prev directory */\r
9018   if (! fSuccess) {\r
9019     return err;\r
9020   }\r
9021 \r
9022   /* Close the handles we don't need in the parent */\r
9023   CloseHandle(piProcInfo.hThread);\r
9024   CloseHandle(hChildStdinRd);\r
9025   CloseHandle(hChildStdoutWr);\r
9026 \r
9027   /* Prepare return value */\r
9028   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9029   cp->kind = CPReal;\r
9030   cp->hProcess = piProcInfo.hProcess;\r
9031   cp->pid = piProcInfo.dwProcessId;\r
9032   cp->hFrom = hChildStdoutRdDup;\r
9033   cp->hTo = hChildStdinWrDup;\r
9034 \r
9035   *pr = (void *) cp;\r
9036 \r
9037   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9038      2000 where engines sometimes don't see the initial command(s)\r
9039      from WinBoard and hang.  I don't understand how that can happen,\r
9040      but the Sleep is harmless, so I've put it in.  Others have also\r
9041      reported what may be the same problem, so hopefully this will fix\r
9042      it for them too.  */\r
9043   Sleep(500);\r
9044 \r
9045   return NO_ERROR;\r
9046 }\r
9047 \r
9048 \r
9049 void\r
9050 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9051 {\r
9052   ChildProc *cp;\r
9053 \r
9054   cp = (ChildProc *) pr;\r
9055   if (cp == NULL) return;\r
9056 \r
9057   switch (cp->kind) {\r
9058   case CPReal:\r
9059     /* TerminateProcess is considered harmful, so... */\r
9060     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9061     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9062     /* The following doesn't work because the chess program\r
9063        doesn't "have the same console" as WinBoard.  Maybe\r
9064        we could arrange for this even though neither WinBoard\r
9065        nor the chess program uses a console for stdio? */\r
9066     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9067 \r
9068     /* [AS] Special termination modes for misbehaving programs... */\r
9069     if( signal == 9 ) {\r
9070         if ( appData.debugMode) {\r
9071             fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
9072         }\r
9073 \r
9074         TerminateProcess( cp->hProcess, 0 );\r
9075     }\r
9076     else if( signal == 10 ) {\r
9077         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9078 \r
9079         if( dw != WAIT_OBJECT_0 ) {\r
9080             if ( appData.debugMode) {\r
9081                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
9082             }\r
9083 \r
9084             TerminateProcess( cp->hProcess, 0 );\r
9085         }\r
9086     }\r
9087 \r
9088     CloseHandle(cp->hProcess);\r
9089     break;\r
9090 \r
9091   case CPComm:\r
9092     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9093     break;\r
9094 \r
9095   case CPSock:\r
9096     closesocket(cp->sock);\r
9097     WSACleanup();\r
9098     break;\r
9099 \r
9100   case CPRcmd:\r
9101     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9102     closesocket(cp->sock);\r
9103     closesocket(cp->sock2);\r
9104     WSACleanup();\r
9105     break;\r
9106   }\r
9107   free(cp);\r
9108 }\r
9109 \r
9110 void\r
9111 InterruptChildProcess(ProcRef pr)\r
9112 {\r
9113   ChildProc *cp;\r
9114 \r
9115   cp = (ChildProc *) pr;\r
9116   if (cp == NULL) return;\r
9117   switch (cp->kind) {\r
9118   case CPReal:\r
9119     /* The following doesn't work because the chess program\r
9120        doesn't "have the same console" as WinBoard.  Maybe\r
9121        we could arrange for this even though neither WinBoard\r
9122        nor the chess program uses a console for stdio */\r
9123     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9124     break;\r
9125 \r
9126   case CPComm:\r
9127   case CPSock:\r
9128     /* Can't interrupt */\r
9129     break;\r
9130 \r
9131   case CPRcmd:\r
9132     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9133     break;\r
9134   }\r
9135 }\r
9136 \r
9137 \r
9138 int\r
9139 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9140 {\r
9141   char cmdLine[MSG_SIZ];\r
9142 \r
9143   if (port[0] == NULLCHAR) {\r
9144     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9145   } else {\r
9146     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9147   }\r
9148   return StartChildProcess(cmdLine, "", pr);\r
9149 }\r
9150 \r
9151 \r
9152 /* Code to open TCP sockets */\r
9153 \r
9154 int\r
9155 OpenTCP(char *host, char *port, ProcRef *pr)\r
9156 {\r
9157   ChildProc *cp;\r
9158   int err;\r
9159   SOCKET s;\r
9160   struct sockaddr_in sa, mysa;\r
9161   struct hostent FAR *hp;\r
9162   unsigned short uport;\r
9163   WORD wVersionRequested;\r
9164   WSADATA wsaData;\r
9165 \r
9166   /* Initialize socket DLL */\r
9167   wVersionRequested = MAKEWORD(1, 1);\r
9168   err = WSAStartup(wVersionRequested, &wsaData);\r
9169   if (err != 0) return err;\r
9170 \r
9171   /* Make socket */\r
9172   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9173     err = WSAGetLastError();\r
9174     WSACleanup();\r
9175     return err;\r
9176   }\r
9177 \r
9178   /* Bind local address using (mostly) don't-care values.\r
9179    */\r
9180   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9181   mysa.sin_family = AF_INET;\r
9182   mysa.sin_addr.s_addr = INADDR_ANY;\r
9183   uport = (unsigned short) 0;\r
9184   mysa.sin_port = htons(uport);\r
9185   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9186       == SOCKET_ERROR) {\r
9187     err = WSAGetLastError();\r
9188     WSACleanup();\r
9189     return err;\r
9190   }\r
9191 \r
9192   /* Resolve remote host name */\r
9193   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9194   if (!(hp = gethostbyname(host))) {\r
9195     unsigned int b0, b1, b2, b3;\r
9196 \r
9197     err = WSAGetLastError();\r
9198 \r
9199     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9200       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9201       hp->h_addrtype = AF_INET;\r
9202       hp->h_length = 4;\r
9203       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9204       hp->h_addr_list[0] = (char *) malloc(4);\r
9205       hp->h_addr_list[0][0] = (char) b0;\r
9206       hp->h_addr_list[0][1] = (char) b1;\r
9207       hp->h_addr_list[0][2] = (char) b2;\r
9208       hp->h_addr_list[0][3] = (char) b3;\r
9209     } else {\r
9210       WSACleanup();\r
9211       return err;\r
9212     }\r
9213   }\r
9214   sa.sin_family = hp->h_addrtype;\r
9215   uport = (unsigned short) atoi(port);\r
9216   sa.sin_port = htons(uport);\r
9217   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9218 \r
9219   /* Make connection */\r
9220   if (connect(s, (struct sockaddr *) &sa,\r
9221               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9222     err = WSAGetLastError();\r
9223     WSACleanup();\r
9224     return err;\r
9225   }\r
9226 \r
9227   /* Prepare return value */\r
9228   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9229   cp->kind = CPSock;\r
9230   cp->sock = s;\r
9231   *pr = (ProcRef *) cp;\r
9232 \r
9233   return NO_ERROR;\r
9234 }\r
9235 \r
9236 int\r
9237 OpenCommPort(char *name, ProcRef *pr)\r
9238 {\r
9239   HANDLE h;\r
9240   COMMTIMEOUTS ct;\r
9241   ChildProc *cp;\r
9242   char fullname[MSG_SIZ];\r
9243 \r
9244   if (*name != '\\')\r
9245     sprintf(fullname, "\\\\.\\%s", name);\r
9246   else\r
9247     strcpy(fullname, name);\r
9248 \r
9249   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9250                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9251   if (h == (HANDLE) -1) {\r
9252     return GetLastError();\r
9253   }\r
9254   hCommPort = h;\r
9255 \r
9256   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9257 \r
9258   /* Accumulate characters until a 100ms pause, then parse */\r
9259   ct.ReadIntervalTimeout = 100;\r
9260   ct.ReadTotalTimeoutMultiplier = 0;\r
9261   ct.ReadTotalTimeoutConstant = 0;\r
9262   ct.WriteTotalTimeoutMultiplier = 0;\r
9263   ct.WriteTotalTimeoutConstant = 0;\r
9264   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9265 \r
9266   /* Prepare return value */\r
9267   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9268   cp->kind = CPComm;\r
9269   cp->hFrom = h;\r
9270   cp->hTo = h;\r
9271   *pr = (ProcRef *) cp;\r
9272 \r
9273   return NO_ERROR;\r
9274 }\r
9275 \r
9276 int\r
9277 OpenLoopback(ProcRef *pr)\r
9278 {\r
9279   DisplayFatalError("Not implemented", 0, 1);\r
9280   return NO_ERROR;\r
9281 }\r
9282 \r
9283 \r
9284 int\r
9285 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9286 {\r
9287   ChildProc *cp;\r
9288   int err;\r
9289   SOCKET s, s2, s3;\r
9290   struct sockaddr_in sa, mysa;\r
9291   struct hostent FAR *hp;\r
9292   unsigned short uport;\r
9293   WORD wVersionRequested;\r
9294   WSADATA wsaData;\r
9295   int fromPort;\r
9296   char stderrPortStr[MSG_SIZ];\r
9297 \r
9298   /* Initialize socket DLL */\r
9299   wVersionRequested = MAKEWORD(1, 1);\r
9300   err = WSAStartup(wVersionRequested, &wsaData);\r
9301   if (err != 0) return err;\r
9302 \r
9303   /* Resolve remote host name */\r
9304   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9305   if (!(hp = gethostbyname(host))) {\r
9306     unsigned int b0, b1, b2, b3;\r
9307 \r
9308     err = WSAGetLastError();\r
9309 \r
9310     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9311       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9312       hp->h_addrtype = AF_INET;\r
9313       hp->h_length = 4;\r
9314       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9315       hp->h_addr_list[0] = (char *) malloc(4);\r
9316       hp->h_addr_list[0][0] = (char) b0;\r
9317       hp->h_addr_list[0][1] = (char) b1;\r
9318       hp->h_addr_list[0][2] = (char) b2;\r
9319       hp->h_addr_list[0][3] = (char) b3;\r
9320     } else {\r
9321       WSACleanup();\r
9322       return err;\r
9323     }\r
9324   }\r
9325   sa.sin_family = hp->h_addrtype;\r
9326   uport = (unsigned short) 514;\r
9327   sa.sin_port = htons(uport);\r
9328   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9329 \r
9330   /* Bind local socket to unused "privileged" port address\r
9331    */\r
9332   s = INVALID_SOCKET;\r
9333   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9334   mysa.sin_family = AF_INET;\r
9335   mysa.sin_addr.s_addr = INADDR_ANY;\r
9336   for (fromPort = 1023;; fromPort--) {\r
9337     if (fromPort < 0) {\r
9338       WSACleanup();\r
9339       return WSAEADDRINUSE;\r
9340     }\r
9341     if (s == INVALID_SOCKET) {\r
9342       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9343         err = WSAGetLastError();\r
9344         WSACleanup();\r
9345         return err;\r
9346       }\r
9347     }\r
9348     uport = (unsigned short) fromPort;\r
9349     mysa.sin_port = htons(uport);\r
9350     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9351         == SOCKET_ERROR) {\r
9352       err = WSAGetLastError();\r
9353       if (err == WSAEADDRINUSE) continue;\r
9354       WSACleanup();\r
9355       return err;\r
9356     }\r
9357     if (connect(s, (struct sockaddr *) &sa,\r
9358       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9359       err = WSAGetLastError();\r
9360       if (err == WSAEADDRINUSE) {\r
9361         closesocket(s);\r
9362         s = -1;\r
9363         continue;\r
9364       }\r
9365       WSACleanup();\r
9366       return err;\r
9367     }\r
9368     break;\r
9369   }\r
9370 \r
9371   /* Bind stderr local socket to unused "privileged" port address\r
9372    */\r
9373   s2 = INVALID_SOCKET;\r
9374   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9375   mysa.sin_family = AF_INET;\r
9376   mysa.sin_addr.s_addr = INADDR_ANY;\r
9377   for (fromPort = 1023;; fromPort--) {\r
9378     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9379     if (fromPort < 0) {\r
9380       (void) closesocket(s);\r
9381       WSACleanup();\r
9382       return WSAEADDRINUSE;\r
9383     }\r
9384     if (s2 == INVALID_SOCKET) {\r
9385       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9386         err = WSAGetLastError();\r
9387         closesocket(s);\r
9388         WSACleanup();\r
9389         return err;\r
9390       }\r
9391     }\r
9392     uport = (unsigned short) fromPort;\r
9393     mysa.sin_port = htons(uport);\r
9394     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9395         == SOCKET_ERROR) {\r
9396       err = WSAGetLastError();\r
9397       if (err == WSAEADDRINUSE) continue;\r
9398       (void) closesocket(s);\r
9399       WSACleanup();\r
9400       return err;\r
9401     }\r
9402     if (listen(s2, 1) == SOCKET_ERROR) {\r
9403       err = WSAGetLastError();\r
9404       if (err == WSAEADDRINUSE) {\r
9405         closesocket(s2);\r
9406         s2 = INVALID_SOCKET;\r
9407         continue;\r
9408       }\r
9409       (void) closesocket(s);\r
9410       (void) closesocket(s2);\r
9411       WSACleanup();\r
9412       return err;\r
9413     }\r
9414     break;\r
9415   }\r
9416   prevStderrPort = fromPort; // remember port used\r
9417   sprintf(stderrPortStr, "%d", fromPort);\r
9418 \r
9419   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9420     err = WSAGetLastError();\r
9421     (void) closesocket(s);\r
9422     (void) closesocket(s2);\r
9423     WSACleanup();\r
9424     return err;\r
9425   }\r
9426 \r
9427   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9428     err = WSAGetLastError();\r
9429     (void) closesocket(s);\r
9430     (void) closesocket(s2);\r
9431     WSACleanup();\r
9432     return err;\r
9433   }\r
9434   if (*user == NULLCHAR) user = UserName();\r
9435   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9436     err = WSAGetLastError();\r
9437     (void) closesocket(s);\r
9438     (void) closesocket(s2);\r
9439     WSACleanup();\r
9440     return err;\r
9441   }\r
9442   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9443     err = WSAGetLastError();\r
9444     (void) closesocket(s);\r
9445     (void) closesocket(s2);\r
9446     WSACleanup();\r
9447     return err;\r
9448   }\r
9449 \r
9450   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9451     err = WSAGetLastError();\r
9452     (void) closesocket(s);\r
9453     (void) closesocket(s2);\r
9454     WSACleanup();\r
9455     return err;\r
9456   }\r
9457   (void) closesocket(s2);  /* Stop listening */\r
9458 \r
9459   /* Prepare return value */\r
9460   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9461   cp->kind = CPRcmd;\r
9462   cp->sock = s;\r
9463   cp->sock2 = s3;\r
9464   *pr = (ProcRef *) cp;\r
9465 \r
9466   return NO_ERROR;\r
9467 }\r
9468 \r
9469 \r
9470 InputSourceRef\r
9471 AddInputSource(ProcRef pr, int lineByLine,\r
9472                InputCallback func, VOIDSTAR closure)\r
9473 {\r
9474   InputSource *is, *is2 = NULL;\r
9475   ChildProc *cp = (ChildProc *) pr;\r
9476 \r
9477   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9478   is->lineByLine = lineByLine;\r
9479   is->func = func;\r
9480   is->closure = closure;\r
9481   is->second = NULL;\r
9482   is->next = is->buf;\r
9483   if (pr == NoProc) {\r
9484     is->kind = CPReal;\r
9485     consoleInputSource = is;\r
9486   } else {\r
9487     is->kind = cp->kind;\r
9488     /* \r
9489         [AS] Try to avoid a race condition if the thread is given control too early:\r
9490         we create all threads suspended so that the is->hThread variable can be\r
9491         safely assigned, then let the threads start with ResumeThread.\r
9492     */\r
9493     switch (cp->kind) {\r
9494     case CPReal:\r
9495       is->hFile = cp->hFrom;\r
9496       cp->hFrom = NULL; /* now owned by InputThread */\r
9497       is->hThread =\r
9498         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9499                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9500       break;\r
9501 \r
9502     case CPComm:\r
9503       is->hFile = cp->hFrom;\r
9504       cp->hFrom = NULL; /* now owned by InputThread */\r
9505       is->hThread =\r
9506         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9507                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9508       break;\r
9509 \r
9510     case CPSock:\r
9511       is->sock = cp->sock;\r
9512       is->hThread =\r
9513         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9514                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9515       break;\r
9516 \r
9517     case CPRcmd:\r
9518       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9519       *is2 = *is;\r
9520       is->sock = cp->sock;\r
9521       is->second = is2;\r
9522       is2->sock = cp->sock2;\r
9523       is2->second = is2;\r
9524       is->hThread =\r
9525         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9526                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9527       is2->hThread =\r
9528         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9529                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9530       break;\r
9531     }\r
9532 \r
9533     if( is->hThread != NULL ) {\r
9534         ResumeThread( is->hThread );\r
9535     }\r
9536 \r
9537     if( is2 != NULL && is2->hThread != NULL ) {\r
9538         ResumeThread( is2->hThread );\r
9539     }\r
9540   }\r
9541 \r
9542   return (InputSourceRef) is;\r
9543 }\r
9544 \r
9545 void\r
9546 RemoveInputSource(InputSourceRef isr)\r
9547 {\r
9548   InputSource *is;\r
9549 \r
9550   is = (InputSource *) isr;\r
9551   is->hThread = NULL;  /* tell thread to stop */\r
9552   CloseHandle(is->hThread);\r
9553   if (is->second != NULL) {\r
9554     is->second->hThread = NULL;\r
9555     CloseHandle(is->second->hThread);\r
9556   }\r
9557 }\r
9558 \r
9559 \r
9560 int\r
9561 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9562 {\r
9563   DWORD dOutCount;\r
9564   int outCount = SOCKET_ERROR;\r
9565   ChildProc *cp = (ChildProc *) pr;\r
9566   static OVERLAPPED ovl;\r
9567 \r
9568   if (pr == NoProc) {\r
9569     ConsoleOutput(message, count, FALSE);\r
9570     return count;\r
9571   } \r
9572 \r
9573   if (ovl.hEvent == NULL) {\r
9574     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9575   }\r
9576   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9577 \r
9578   switch (cp->kind) {\r
9579   case CPSock:\r
9580   case CPRcmd:\r
9581     outCount = send(cp->sock, message, count, 0);\r
9582     if (outCount == SOCKET_ERROR) {\r
9583       *outError = WSAGetLastError();\r
9584     } else {\r
9585       *outError = NO_ERROR;\r
9586     }\r
9587     break;\r
9588 \r
9589   case CPReal:\r
9590     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9591                   &dOutCount, NULL)) {\r
9592       *outError = NO_ERROR;\r
9593       outCount = (int) dOutCount;\r
9594     } else {\r
9595       *outError = GetLastError();\r
9596     }\r
9597     break;\r
9598 \r
9599   case CPComm:\r
9600     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9601                             &dOutCount, &ovl);\r
9602     if (*outError == NO_ERROR) {\r
9603       outCount = (int) dOutCount;\r
9604     }\r
9605     break;\r
9606   }\r
9607   return outCount;\r
9608 }\r
9609 \r
9610 int\r
9611 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9612                        long msdelay)\r
9613 {\r
9614   /* Ignore delay, not implemented for WinBoard */\r
9615   return OutputToProcess(pr, message, count, outError);\r
9616 }\r
9617 \r
9618 \r
9619 void\r
9620 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9621                         char *buf, int count, int error)\r
9622 {\r
9623   DisplayFatalError("Not implemented", 0, 1);\r
9624 }\r
9625 \r
9626 /* see wgamelist.c for Game List functions */\r
9627 /* see wedittags.c for Edit Tags functions */\r
9628 \r
9629 \r
9630 VOID\r
9631 ICSInitScript()\r
9632 {\r
9633   FILE *f;\r
9634   char buf[MSG_SIZ];\r
9635   char *dummy;\r
9636 \r
9637   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9638     f = fopen(buf, "r");\r
9639     if (f != NULL) {\r
9640       ProcessICSInitScript(f);\r
9641       fclose(f);\r
9642     }\r
9643   }\r
9644 }\r
9645 \r
9646 \r
9647 VOID\r
9648 StartAnalysisClock()\r
9649 {\r
9650   if (analysisTimerEvent) return;\r
9651   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9652                                         (UINT) 2000, NULL);\r
9653 }\r
9654 \r
9655 LRESULT CALLBACK\r
9656 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9657 {\r
9658   static HANDLE hwndText;\r
9659   RECT rect;\r
9660   static int sizeX, sizeY;\r
9661   int newSizeX, newSizeY, flags;\r
9662   MINMAXINFO *mmi;\r
9663 \r
9664   switch (message) {\r
9665   case WM_INITDIALOG: /* message: initialize dialog box */\r
9666     /* Initialize the dialog items */\r
9667     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9668     SetWindowText(hDlg, analysisTitle);\r
9669     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9670     /* Size and position the dialog */\r
9671     if (!analysisDialog) {\r
9672       analysisDialog = hDlg;\r
9673       flags = SWP_NOZORDER;\r
9674       GetClientRect(hDlg, &rect);\r
9675       sizeX = rect.right;\r
9676       sizeY = rect.bottom;\r
9677       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9678           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9679         WINDOWPLACEMENT wp;\r
9680         EnsureOnScreen(&analysisX, &analysisY);\r
9681         wp.length = sizeof(WINDOWPLACEMENT);\r
9682         wp.flags = 0;\r
9683         wp.showCmd = SW_SHOW;\r
9684         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9685         wp.rcNormalPosition.left = analysisX;\r
9686         wp.rcNormalPosition.right = analysisX + analysisW;\r
9687         wp.rcNormalPosition.top = analysisY;\r
9688         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9689         SetWindowPlacement(hDlg, &wp);\r
9690 \r
9691         GetClientRect(hDlg, &rect);\r
9692         newSizeX = rect.right;\r
9693         newSizeY = rect.bottom;\r
9694         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9695                               newSizeX, newSizeY);\r
9696         sizeX = newSizeX;\r
9697         sizeY = newSizeY;\r
9698       }\r
9699     }\r
9700     return FALSE;\r
9701 \r
9702   case WM_COMMAND: /* message: received a command */\r
9703     switch (LOWORD(wParam)) {\r
9704     case IDCANCEL:\r
9705       EditGameEvent();\r
9706       return TRUE;\r
9707     default:\r
9708       break;\r
9709     }\r
9710     break;\r
9711 \r
9712   case WM_SIZE:\r
9713     newSizeX = LOWORD(lParam);\r
9714     newSizeY = HIWORD(lParam);\r
9715     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9716     sizeX = newSizeX;\r
9717     sizeY = newSizeY;\r
9718     break;\r
9719 \r
9720   case WM_GETMINMAXINFO:\r
9721     /* Prevent resizing window too small */\r
9722     mmi = (MINMAXINFO *) lParam;\r
9723     mmi->ptMinTrackSize.x = 100;\r
9724     mmi->ptMinTrackSize.y = 100;\r
9725     break;\r
9726   }\r
9727   return FALSE;\r
9728 }\r
9729 \r
9730 VOID\r
9731 AnalysisPopUp(char* title, char* str)\r
9732 {\r
9733   FARPROC lpProc;\r
9734   char *p, *q;\r
9735 \r
9736   /* [AS] */\r
9737   EngineOutputPopUp();\r
9738   return;\r
9739 \r
9740   if (str == NULL) str = "";\r
9741   p = (char *) malloc(2 * strlen(str) + 2);\r
9742   q = p;\r
9743   while (*str) {\r
9744     if (*str == '\n') *q++ = '\r';\r
9745     *q++ = *str++;\r
9746   }\r
9747   *q = NULLCHAR;\r
9748   if (analysisText != NULL) free(analysisText);\r
9749   analysisText = p;\r
9750 \r
9751   if (analysisDialog) {\r
9752     SetWindowText(analysisDialog, title);\r
9753     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9754     ShowWindow(analysisDialog, SW_SHOW);\r
9755   } else {\r
9756     analysisTitle = title;\r
9757     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9758     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9759                  hwndMain, (DLGPROC)lpProc);\r
9760     FreeProcInstance(lpProc);\r
9761   }\r
9762   analysisDialogUp = TRUE;  \r
9763 }\r
9764 \r
9765 VOID\r
9766 AnalysisPopDown()\r
9767 {\r
9768   if (analysisDialog) {\r
9769     ShowWindow(analysisDialog, SW_HIDE);\r
9770   }\r
9771   analysisDialogUp = FALSE;  \r
9772 }\r
9773 \r
9774 \r
9775 VOID\r
9776 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9777 {\r
9778   highlightInfo.sq[0].x = fromX;\r
9779   highlightInfo.sq[0].y = fromY;\r
9780   highlightInfo.sq[1].x = toX;\r
9781   highlightInfo.sq[1].y = toY;\r
9782 }\r
9783 \r
9784 VOID\r
9785 ClearHighlights()\r
9786 {\r
9787   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9788     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9789 }\r
9790 \r
9791 VOID\r
9792 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9793 {\r
9794   premoveHighlightInfo.sq[0].x = fromX;\r
9795   premoveHighlightInfo.sq[0].y = fromY;\r
9796   premoveHighlightInfo.sq[1].x = toX;\r
9797   premoveHighlightInfo.sq[1].y = toY;\r
9798 }\r
9799 \r
9800 VOID\r
9801 ClearPremoveHighlights()\r
9802 {\r
9803   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9804     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9805 }\r
9806 \r
9807 VOID\r
9808 ShutDownFrontEnd()\r
9809 {\r
9810   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9811   DeleteClipboardTempFiles();\r
9812 }\r
9813 \r
9814 void\r
9815 BoardToTop()\r
9816 {\r
9817     if (IsIconic(hwndMain))\r
9818       ShowWindow(hwndMain, SW_RESTORE);\r
9819 \r
9820     SetActiveWindow(hwndMain);\r
9821 }\r
9822 \r
9823 /*\r
9824  * Prototypes for animation support routines\r
9825  */\r
9826 static void ScreenSquare(int column, int row, POINT * pt);\r
9827 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9828      POINT frames[], int * nFrames);\r
9829 \r
9830 \r
9831 #define kFactor 4\r
9832 \r
9833 void\r
9834 AnimateMove(board, fromX, fromY, toX, toY)\r
9835      Board board;\r
9836      int fromX;\r
9837      int fromY;\r
9838      int toX;\r
9839      int toY;\r
9840 {\r
9841   ChessSquare piece;\r
9842   POINT start, finish, mid;\r
9843   POINT frames[kFactor * 2 + 1];\r
9844   int nFrames, n;\r
9845 \r
9846   if (!appData.animate) return;\r
9847   if (doingSizing) return;\r
9848   if (fromY < 0 || fromX < 0) return;\r
9849   piece = board[fromY][fromX];\r
9850   if (piece >= EmptySquare) return;\r
9851 \r
9852   ScreenSquare(fromX, fromY, &start);\r
9853   ScreenSquare(toX, toY, &finish);\r
9854 \r
9855   /* All pieces except knights move in straight line */\r
9856   if (piece != WhiteKnight && piece != BlackKnight) {\r
9857     mid.x = start.x + (finish.x - start.x) / 2;\r
9858     mid.y = start.y + (finish.y - start.y) / 2;\r
9859   } else {\r
9860     /* Knight: make diagonal movement then straight */\r
9861     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9862        mid.x = start.x + (finish.x - start.x) / 2;\r
9863        mid.y = finish.y;\r
9864      } else {\r
9865        mid.x = finish.x;\r
9866        mid.y = start.y + (finish.y - start.y) / 2;\r
9867      }\r
9868   }\r
9869   \r
9870   /* Don't use as many frames for very short moves */\r
9871   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9872     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9873   else\r
9874     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9875 \r
9876   animInfo.from.x = fromX;\r
9877   animInfo.from.y = fromY;\r
9878   animInfo.to.x = toX;\r
9879   animInfo.to.y = toY;\r
9880   animInfo.lastpos = start;\r
9881   animInfo.piece = piece;\r
9882   for (n = 0; n < nFrames; n++) {\r
9883     animInfo.pos = frames[n];\r
9884     DrawPosition(FALSE, NULL);\r
9885     animInfo.lastpos = animInfo.pos;\r
9886     Sleep(appData.animSpeed);\r
9887   }\r
9888   animInfo.pos = finish;\r
9889   DrawPosition(FALSE, NULL);\r
9890   animInfo.piece = EmptySquare;\r
9891 }\r
9892 \r
9893 /*      Convert board position to corner of screen rect and color       */\r
9894 \r
9895 static void\r
9896 ScreenSquare(column, row, pt)\r
9897      int column; int row; POINT * pt;\r
9898 {\r
9899   if (flipView) {\r
9900     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9901     pt->y = lineGap + row * (squareSize + lineGap);\r
9902   } else {\r
9903     pt->x = lineGap + column * (squareSize + lineGap);\r
9904     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9905   }\r
9906 }\r
9907 \r
9908 /*      Generate a series of frame coords from start->mid->finish.\r
9909         The movement rate doubles until the half way point is\r
9910         reached, then halves back down to the final destination,\r
9911         which gives a nice slow in/out effect. The algorithmn\r
9912         may seem to generate too many intermediates for short\r
9913         moves, but remember that the purpose is to attract the\r
9914         viewers attention to the piece about to be moved and\r
9915         then to where it ends up. Too few frames would be less\r
9916         noticeable.                                             */\r
9917 \r
9918 static void\r
9919 Tween(start, mid, finish, factor, frames, nFrames)\r
9920      POINT * start; POINT * mid;\r
9921      POINT * finish; int factor;\r
9922      POINT frames[]; int * nFrames;\r
9923 {\r
9924   int n, fraction = 1, count = 0;\r
9925 \r
9926   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9927   for (n = 0; n < factor; n++)\r
9928     fraction *= 2;\r
9929   for (n = 0; n < factor; n++) {\r
9930     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9931     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9932     count ++;\r
9933     fraction = fraction / 2;\r
9934   }\r
9935   \r
9936   /* Midpoint */\r
9937   frames[count] = *mid;\r
9938   count ++;\r
9939   \r
9940   /* Slow out, stepping 1/2, then 1/4, ... */\r
9941   fraction = 2;\r
9942   for (n = 0; n < factor; n++) {\r
9943     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9944     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9945     count ++;\r
9946     fraction = fraction * 2;\r
9947   }\r
9948   *nFrames = count;\r
9949 }\r
9950 \r
9951 void\r
9952 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9953 {\r
9954 #if 0\r
9955     char buf[256];\r
9956 \r
9957     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
9958         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
9959 \r
9960     OutputDebugString( buf );\r
9961 #endif\r
9962 \r
9963     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9964 \r
9965     EvalGraphSet( first, last, current, pvInfoList );\r
9966 }\r
9967 \r
9968 void SetProgramStats( FrontEndProgramStats * stats )\r
9969 {\r
9970 #if 0\r
9971     char buf[1024];\r
9972 \r
9973     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
9974         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
9975 \r
9976     OutputDebugString( buf );\r
9977 #endif\r
9978 \r
9979     EngineOutputUpdate( stats );\r
9980 }\r