a96aeb6cd25d70e01a3c5f7aa54da42624979aea
[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.upsideDown, 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   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3175   /* special Shogi support in this size */\r
3176   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3177       for (piece = WhitePawn;\r
3178            (int) piece < (int) BlackPawn;\r
3179            piece = (ChessSquare) ((int) piece + 1)) {\r
3180         if (pieceBitmap[i][piece] != NULL)\r
3181           DeleteObject(pieceBitmap[i][piece]);\r
3182       }\r
3183     }\r
3184   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3185   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3186   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3187   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3188   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3189   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3190   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3191   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3192   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3193   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3194   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3195   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3196   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3197   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3198   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3199   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3200   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3201   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3202   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3203   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3204   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3205   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3206   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3207   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3208   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3209   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3210   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3211   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3212   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3213   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3214   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3215   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3216   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3217   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3218   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3219   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3220   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3221   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3222   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3223   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3224   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3225   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3226   minorSize = 0;\r
3227   }\r
3228 }\r
3229 \r
3230 HBITMAP\r
3231 PieceBitmap(ChessSquare p, int kind)\r
3232 {\r
3233   if ((int) p >= (int) BlackPawn)\r
3234     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3235 \r
3236   return pieceBitmap[kind][(int) p];\r
3237 }\r
3238 \r
3239 /***************************************************************/\r
3240 \r
3241 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3242 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3243 /*\r
3244 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3245 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3246 */\r
3247 \r
3248 VOID\r
3249 SquareToPos(int row, int column, int * x, int * y)\r
3250 {\r
3251   if (flipView) {\r
3252     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3253     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3254   } else {\r
3255     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3256     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3257   }\r
3258 }\r
3259 \r
3260 VOID\r
3261 DrawCoordsOnDC(HDC hdc)\r
3262 {\r
3263   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
3264   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
3265   char str[2] = { NULLCHAR, NULLCHAR };\r
3266   int oldMode, oldAlign, x, y, start, i;\r
3267   HFONT oldFont;\r
3268   HBRUSH oldBrush;\r
3269 \r
3270   if (!appData.showCoords)\r
3271     return;\r
3272 \r
3273   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3274 \r
3275   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3276   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3277   oldAlign = GetTextAlign(hdc);\r
3278   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3279 \r
3280   y = boardRect.top + lineGap;\r
3281   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3282 \r
3283   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3284   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3285     str[0] = files[start + i];\r
3286     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3287     y += squareSize + lineGap;\r
3288   }\r
3289 \r
3290   start = flipView ? 12-BOARD_WIDTH : 12;\r
3291 \r
3292   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3293   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3294     str[0] = ranks[start + i];\r
3295     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3296     x += squareSize + lineGap;\r
3297   }    \r
3298 \r
3299   SelectObject(hdc, oldBrush);\r
3300   SetBkMode(hdc, oldMode);\r
3301   SetTextAlign(hdc, oldAlign);\r
3302   SelectObject(hdc, oldFont);\r
3303 }\r
3304 \r
3305 VOID\r
3306 DrawGridOnDC(HDC hdc)\r
3307 {\r
3308   HPEN oldPen;\r
3309  \r
3310   if (lineGap != 0) {\r
3311     oldPen = SelectObject(hdc, gridPen);\r
3312     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3313     SelectObject(hdc, oldPen);\r
3314   }\r
3315 }\r
3316 \r
3317 #define HIGHLIGHT_PEN 0\r
3318 #define PREMOVE_PEN   1\r
3319 \r
3320 VOID\r
3321 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3322 {\r
3323   int x1, y1;\r
3324   HPEN oldPen, hPen;\r
3325   if (lineGap == 0) return;\r
3326   if (flipView) {\r
3327     x1 = boardRect.left +\r
3328       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3329     y1 = boardRect.top +\r
3330       lineGap/2 + y * (squareSize + lineGap);\r
3331   } else {\r
3332     x1 = boardRect.left +\r
3333       lineGap/2 + x * (squareSize + lineGap);\r
3334     y1 = boardRect.top +\r
3335       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3336   }\r
3337   hPen = pen ? premovePen : highlightPen;\r
3338   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3339   MoveToEx(hdc, x1, y1, NULL);\r
3340   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3341   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3342   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3343   LineTo(hdc, x1, y1);\r
3344   SelectObject(hdc, oldPen);\r
3345 }\r
3346 \r
3347 VOID\r
3348 DrawHighlightsOnDC(HDC hdc)\r
3349 {\r
3350   int i;\r
3351   for (i=0; i<2; i++) {\r
3352     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3353       DrawHighlightOnDC(hdc, TRUE,\r
3354                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3355                         HIGHLIGHT_PEN);\r
3356   }\r
3357   for (i=0; i<2; i++) {\r
3358     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3359         premoveHighlightInfo.sq[i].y >= 0) {\r
3360         DrawHighlightOnDC(hdc, TRUE,\r
3361                           premoveHighlightInfo.sq[i].x, \r
3362                           premoveHighlightInfo.sq[i].y,\r
3363                           PREMOVE_PEN);\r
3364     }\r
3365   }\r
3366 }\r
3367 \r
3368 /* Note: sqcolor is used only in monoMode */\r
3369 /* Note that this code is largely duplicated in woptions.c,\r
3370    function DrawSampleSquare, so that needs to be updated too */\r
3371 VOID\r
3372 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3373 {\r
3374   HBITMAP oldBitmap;\r
3375   HBRUSH oldBrush;\r
3376   int tmpSize;\r
3377 \r
3378   if (appData.blindfold) return;\r
3379 \r
3380   /* [AS] Use font-based pieces if needed */\r
3381   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3382     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3383     CreatePiecesFromFont();\r
3384 \r
3385     if( fontBitmapSquareSize == squareSize ) {\r
3386         int index = TranslatePieceToFontPiece( piece );\r
3387 \r
3388         SelectObject( tmphdc, hPieceMask[ index ] );\r
3389 \r
3390         BitBlt( hdc,\r
3391             x, y,\r
3392             squareSize, squareSize,\r
3393             tmphdc,\r
3394             0, 0,\r
3395             SRCAND );\r
3396 \r
3397         SelectObject( tmphdc, hPieceFace[ index ] );\r
3398 \r
3399         BitBlt( hdc,\r
3400             x, y,\r
3401             squareSize, squareSize,\r
3402             tmphdc,\r
3403             0, 0,\r
3404             SRCPAINT );\r
3405 \r
3406         return;\r
3407     }\r
3408   }\r
3409 \r
3410   if (appData.monoMode) {\r
3411     SelectObject(tmphdc, PieceBitmap(piece, \r
3412       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3413     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3414            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3415   } else {\r
3416     tmpSize = squareSize;\r
3417     if(minorSize &&\r
3418         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3419          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3420       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3421       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3422       x += (squareSize - minorSize)>>1;\r
3423       y += squareSize - minorSize - 2;\r
3424       tmpSize = minorSize;\r
3425     }\r
3426     if (color || appData.allWhite ) {\r
3427       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3428       if( color )\r
3429               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3430       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3431       if(appData.upsideDown && !color)\r
3432         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3433       else\r
3434         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3435 #if 0\r
3436       /* Use black piece color for outline of white pieces */\r
3437       /* Not sure this looks really good (though xboard does it).\r
3438          Maybe better to have another selectable color, default black */\r
3439       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3440       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3441       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3442 #else\r
3443       /* Use black for outline of white pieces */\r
3444       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3445       if(appData.upsideDown && !color)\r
3446         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3447       else\r
3448         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3449 #endif\r
3450     } else {\r
3451 #if 0\r
3452       /* Use white piece color for details of black pieces */\r
3453       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3454          WHITE_PIECE ones aren't always the right shape. */\r
3455       /* Not sure this looks really good (though xboard does it).\r
3456          Maybe better to have another selectable color, default medium gray? */\r
3457       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3458       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3459       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3460       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3461       SelectObject(hdc, blackPieceBrush);\r
3462       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3463 #else\r
3464       /* Use square color for details of black pieces */\r
3465       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3466       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3467       if(appData.upsideDown)\r
3468         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3469       else\r
3470         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3471 #endif\r
3472     }\r
3473     SelectObject(hdc, oldBrush);\r
3474     SelectObject(tmphdc, oldBitmap);\r
3475   }\r
3476 }\r
3477 \r
3478 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3479 int GetBackTextureMode( int algo )\r
3480 {\r
3481     int result = BACK_TEXTURE_MODE_DISABLED;\r
3482 \r
3483     switch( algo ) \r
3484     {\r
3485         case BACK_TEXTURE_MODE_PLAIN:\r
3486             result = 1; /* Always use identity map */\r
3487             break;\r
3488         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3489             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3490             break;\r
3491     }\r
3492 \r
3493     return result;\r
3494 }\r
3495 \r
3496 /* \r
3497     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3498     to handle redraws cleanly (as random numbers would always be different).\r
3499 */\r
3500 VOID RebuildTextureSquareInfo()\r
3501 {\r
3502     BITMAP bi;\r
3503     int lite_w = 0;\r
3504     int lite_h = 0;\r
3505     int dark_w = 0;\r
3506     int dark_h = 0;\r
3507     int row;\r
3508     int col;\r
3509 \r
3510     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3511 \r
3512     if( liteBackTexture != NULL ) {\r
3513         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3514             lite_w = bi.bmWidth;\r
3515             lite_h = bi.bmHeight;\r
3516         }\r
3517     }\r
3518 \r
3519     if( darkBackTexture != NULL ) {\r
3520         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3521             dark_w = bi.bmWidth;\r
3522             dark_h = bi.bmHeight;\r
3523         }\r
3524     }\r
3525 \r
3526     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3527         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3528             if( (col + row) & 1 ) {\r
3529                 /* Lite square */\r
3530                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3531                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3532                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3533                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3534                 }\r
3535             }\r
3536             else {\r
3537                 /* Dark square */\r
3538                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3539                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3540                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3541                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3542                 }\r
3543             }\r
3544         }\r
3545     }\r
3546 }\r
3547 \r
3548 /* [AS] Arrow highlighting support */\r
3549 \r
3550 static int A_WIDTH = 5; /* Width of arrow body */\r
3551 \r
3552 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3553 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3554 \r
3555 static double Sqr( double x )\r
3556 {\r
3557     return x*x;\r
3558 }\r
3559 \r
3560 static int Round( double x )\r
3561 {\r
3562     return (int) (x + 0.5);\r
3563 }\r
3564 \r
3565 /* Draw an arrow between two points using current settings */\r
3566 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3567 {\r
3568     POINT arrow[7];\r
3569     double dx, dy, j, k, x, y;\r
3570 \r
3571     if( d_x == s_x ) {\r
3572         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3573 \r
3574         arrow[0].x = s_x + A_WIDTH;\r
3575         arrow[0].y = s_y;\r
3576 \r
3577         arrow[1].x = s_x + A_WIDTH;\r
3578         arrow[1].y = d_y - h;\r
3579 \r
3580         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3581         arrow[2].y = d_y - h;\r
3582 \r
3583         arrow[3].x = d_x;\r
3584         arrow[3].y = d_y;\r
3585 \r
3586         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3587         arrow[4].y = d_y - h;\r
3588 \r
3589         arrow[5].x = s_x - A_WIDTH;\r
3590         arrow[5].y = d_y - h;\r
3591 \r
3592         arrow[6].x = s_x - A_WIDTH;\r
3593         arrow[6].y = s_y;\r
3594     }\r
3595     else if( d_y == s_y ) {\r
3596         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3597 \r
3598         arrow[0].x = s_x;\r
3599         arrow[0].y = s_y + A_WIDTH;\r
3600 \r
3601         arrow[1].x = d_x - w;\r
3602         arrow[1].y = s_y + A_WIDTH;\r
3603 \r
3604         arrow[2].x = d_x - w;\r
3605         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3606 \r
3607         arrow[3].x = d_x;\r
3608         arrow[3].y = d_y;\r
3609 \r
3610         arrow[4].x = d_x - w;\r
3611         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3612 \r
3613         arrow[5].x = d_x - w;\r
3614         arrow[5].y = s_y - A_WIDTH;\r
3615 \r
3616         arrow[6].x = s_x;\r
3617         arrow[6].y = s_y - A_WIDTH;\r
3618     }\r
3619     else {\r
3620         /* [AS] Needed a lot of paper for this! :-) */\r
3621         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3622         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3623   \r
3624         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3625 \r
3626         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3627 \r
3628         x = s_x;\r
3629         y = s_y;\r
3630 \r
3631         arrow[0].x = Round(x - j);\r
3632         arrow[0].y = Round(y + j*dx);\r
3633 \r
3634         arrow[1].x = Round(x + j);\r
3635         arrow[1].y = Round(y - j*dx);\r
3636 \r
3637         if( d_x > s_x ) {\r
3638             x = (double) d_x - k;\r
3639             y = (double) d_y - k*dy;\r
3640         }\r
3641         else {\r
3642             x = (double) d_x + k;\r
3643             y = (double) d_y + k*dy;\r
3644         }\r
3645 \r
3646         arrow[2].x = Round(x + j);\r
3647         arrow[2].y = Round(y - j*dx);\r
3648 \r
3649         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3650         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3651 \r
3652         arrow[4].x = d_x;\r
3653         arrow[4].y = d_y;\r
3654 \r
3655         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3656         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3657 \r
3658         arrow[6].x = Round(x - j);\r
3659         arrow[6].y = Round(y + j*dx);\r
3660     }\r
3661 \r
3662     Polygon( hdc, arrow, 7 );\r
3663 }\r
3664 \r
3665 /* [AS] Draw an arrow between two squares */\r
3666 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3667 {\r
3668     int s_x, s_y, d_x, d_y;\r
3669     HPEN hpen;\r
3670     HPEN holdpen;\r
3671     HBRUSH hbrush;\r
3672     HBRUSH holdbrush;\r
3673     LOGBRUSH stLB;\r
3674 \r
3675     if( s_col == d_col && s_row == d_row ) {\r
3676         return;\r
3677     }\r
3678 \r
3679     /* Get source and destination points */\r
3680     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3681     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3682 \r
3683     if( d_y > s_y ) {\r
3684         d_y += squareSize / 4;\r
3685     }\r
3686     else if( d_y < s_y ) {\r
3687         d_y += 3 * squareSize / 4;\r
3688     }\r
3689     else {\r
3690         d_y += squareSize / 2;\r
3691     }\r
3692 \r
3693     if( d_x > s_x ) {\r
3694         d_x += squareSize / 4;\r
3695     }\r
3696     else if( d_x < s_x ) {\r
3697         d_x += 3 * squareSize / 4;\r
3698     }\r
3699     else {\r
3700         d_x += squareSize / 2;\r
3701     }\r
3702 \r
3703     s_x += squareSize / 2;\r
3704     s_y += squareSize / 2;\r
3705 \r
3706     /* Adjust width */\r
3707     A_WIDTH = squareSize / 14;\r
3708 \r
3709     /* Draw */\r
3710     stLB.lbStyle = BS_SOLID;\r
3711     stLB.lbColor = appData.highlightArrowColor;\r
3712     stLB.lbHatch = 0;\r
3713 \r
3714     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3715     holdpen = SelectObject( hdc, hpen );\r
3716     hbrush = CreateBrushIndirect( &stLB );\r
3717     holdbrush = SelectObject( hdc, hbrush );\r
3718 \r
3719     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3720 \r
3721     SelectObject( hdc, holdpen );\r
3722     SelectObject( hdc, holdbrush );\r
3723     DeleteObject( hpen );\r
3724     DeleteObject( hbrush );\r
3725 }\r
3726 \r
3727 BOOL HasHighlightInfo()\r
3728 {\r
3729     BOOL result = FALSE;\r
3730 \r
3731     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3732         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3733     {\r
3734         result = TRUE;\r
3735     }\r
3736 \r
3737     return result;\r
3738 }\r
3739 \r
3740 BOOL IsDrawArrowEnabled()\r
3741 {\r
3742     BOOL result = FALSE;\r
3743 \r
3744     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3745         result = TRUE;\r
3746     }\r
3747 \r
3748     return result;\r
3749 }\r
3750 \r
3751 VOID DrawArrowHighlight( HDC hdc )\r
3752 {\r
3753     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3754         DrawArrowBetweenSquares( hdc,\r
3755             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3756             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3757     }\r
3758 }\r
3759 \r
3760 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3761 {\r
3762     HRGN result = NULL;\r
3763 \r
3764     if( HasHighlightInfo() ) {\r
3765         int x1, y1, x2, y2;\r
3766         int sx, sy, dx, dy;\r
3767 \r
3768         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3769         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3770 \r
3771         sx = MIN( x1, x2 );\r
3772         sy = MIN( y1, y2 );\r
3773         dx = MAX( x1, x2 ) + squareSize;\r
3774         dy = MAX( y1, y2 ) + squareSize;\r
3775 \r
3776         result = CreateRectRgn( sx, sy, dx, dy );\r
3777     }\r
3778 \r
3779     return result;\r
3780 }\r
3781 \r
3782 /*\r
3783     Warning: this function modifies the behavior of several other functions. \r
3784     \r
3785     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3786     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3787     repaint is scattered all over the place, which is not good for features such as\r
3788     "arrow highlighting" that require a full repaint of the board.\r
3789 \r
3790     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3791     user interaction, when speed is not so important) but especially to avoid errors\r
3792     in the displayed graphics.\r
3793 \r
3794     In such patched places, I always try refer to this function so there is a single\r
3795     place to maintain knowledge.\r
3796     \r
3797     To restore the original behavior, just return FALSE unconditionally.\r
3798 */\r
3799 BOOL IsFullRepaintPreferrable()\r
3800 {\r
3801     BOOL result = FALSE;\r
3802 \r
3803     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3804         /* Arrow may appear on the board */\r
3805         result = TRUE;\r
3806     }\r
3807 \r
3808     return result;\r
3809 }\r
3810 \r
3811 /* \r
3812     This function is called by DrawPosition to know whether a full repaint must\r
3813     be forced or not.\r
3814 \r
3815     Only DrawPosition may directly call this function, which makes use of \r
3816     some state information. Other function should call DrawPosition specifying \r
3817     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3818 */\r
3819 BOOL DrawPositionNeedsFullRepaint()\r
3820 {\r
3821     BOOL result = FALSE;\r
3822 \r
3823     /* \r
3824         Probably a slightly better policy would be to trigger a full repaint\r
3825         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3826         but animation is fast enough that it's difficult to notice.\r
3827     */\r
3828     if( animInfo.piece == EmptySquare ) {\r
3829         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3830             result = TRUE;\r
3831         }\r
3832     }\r
3833 \r
3834     return result;\r
3835 }\r
3836 \r
3837 VOID\r
3838 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3839 {\r
3840   int row, column, x, y, square_color, piece_color;\r
3841   ChessSquare piece;\r
3842   HBRUSH oldBrush;\r
3843   HDC texture_hdc = NULL;\r
3844 \r
3845   /* [AS] Initialize background textures if needed */\r
3846   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3847       if( backTextureSquareSize != squareSize ) {\r
3848           backTextureSquareSize = squareSize;\r
3849           RebuildTextureSquareInfo();\r
3850       }\r
3851 \r
3852       texture_hdc = CreateCompatibleDC( hdc );\r
3853   }\r
3854 \r
3855   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3856     for (column = 0; column < BOARD_WIDTH; column++) {\r
3857   \r
3858       SquareToPos(row, column, &x, &y);\r
3859 \r
3860       piece = board[row][column];\r
3861 \r
3862       square_color = ((column + row) % 2) == 1;\r
3863       if(!strcmp(appData.variant, "xiangqi") ) {\r
3864           square_color = !InPalace(row, column);\r
3865           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3866           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3867       }\r
3868       piece_color = (int) piece < (int) BlackPawn;\r
3869 \r
3870 \r
3871 #ifdef FAIRY\r
3872       /* [HGM] holdings file: light square or black */\r
3873       if(column == BOARD_LEFT-2) {\r
3874             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3875                 square_color = 1;\r
3876             else {\r
3877                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3878                 continue;\r
3879             }\r
3880       } else\r
3881       if(column == BOARD_RGHT + 1 ) {\r
3882             if( row < gameInfo.holdingsSize )\r
3883                 square_color = 1;\r
3884             else {\r
3885                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3886                 continue;\r
3887             }\r
3888       }\r
3889       if(column == BOARD_LEFT-1 ) /* left align */\r
3890             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3891       else if( column == BOARD_RGHT) /* right align */\r
3892             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3893       else\r
3894 #endif\r
3895       if (appData.monoMode) {\r
3896         if (piece == EmptySquare) {\r
3897           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3898                  square_color ? WHITENESS : BLACKNESS);\r
3899         } else {\r
3900           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3901         }\r
3902       } \r
3903       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3904           /* [AS] Draw the square using a texture bitmap */\r
3905           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3906 \r
3907           DrawTile( x, y, \r
3908               squareSize, squareSize, \r
3909               hdc, \r
3910               texture_hdc,\r
3911               backTextureSquareInfo[row][column].mode,\r
3912               backTextureSquareInfo[row][column].x,\r
3913               backTextureSquareInfo[row][column].y );\r
3914 \r
3915           SelectObject( texture_hdc, hbm );\r
3916 \r
3917           if (piece != EmptySquare) {\r
3918               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3919           }\r
3920       }\r
3921       else {\r
3922         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3923 \r
3924         oldBrush = SelectObject(hdc, brush );\r
3925         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3926         SelectObject(hdc, oldBrush);\r
3927         if (piece != EmptySquare)\r
3928           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3929       }\r
3930     }\r
3931   }\r
3932 \r
3933   if( texture_hdc != NULL ) {\r
3934     DeleteDC( texture_hdc );\r
3935   }\r
3936 }\r
3937 \r
3938 #define MAX_CLIPS 200   /* more than enough */\r
3939 \r
3940 VOID\r
3941 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3942 {\r
3943   static Board lastReq, lastDrawn;\r
3944   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3945   static int lastDrawnFlipView = 0;\r
3946   static int lastReqValid = 0, lastDrawnValid = 0;\r
3947   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3948   HDC tmphdc;\r
3949   HDC hdcmem;\r
3950   HBITMAP bufferBitmap;\r
3951   HBITMAP oldBitmap;\r
3952   RECT Rect;\r
3953   HRGN clips[MAX_CLIPS];\r
3954   ChessSquare dragged_piece = EmptySquare;\r
3955 \r
3956   /* I'm undecided on this - this function figures out whether a full\r
3957    * repaint is necessary on its own, so there's no real reason to have the\r
3958    * caller tell it that.  I think this can safely be set to FALSE - but\r
3959    * if we trust the callers not to request full repaints unnessesarily, then\r
3960    * we could skip some clipping work.  In other words, only request a full\r
3961    * redraw when the majority of pieces have changed positions (ie. flip, \r
3962    * gamestart and similar)  --Hawk\r
3963    */\r
3964   Boolean fullrepaint = repaint;\r
3965 \r
3966   if( DrawPositionNeedsFullRepaint() ) {\r
3967       fullrepaint = TRUE;\r
3968   }\r
3969 \r
3970 #if 0\r
3971   if( fullrepaint ) {\r
3972       static int repaint_count = 0;\r
3973       char buf[128];\r
3974 \r
3975       repaint_count++;\r
3976       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
3977       OutputDebugString( buf );\r
3978   }\r
3979 #endif\r
3980 \r
3981   if (board == NULL) {\r
3982     if (!lastReqValid) {\r
3983       return;\r
3984     }\r
3985     board = lastReq;\r
3986   } else {\r
3987     CopyBoard(lastReq, board);\r
3988     lastReqValid = 1;\r
3989   }\r
3990 \r
3991   if (doingSizing) {\r
3992     return;\r
3993   }\r
3994 \r
3995   if (IsIconic(hwndMain)) {\r
3996     return;\r
3997   }\r
3998 \r
3999   if (hdc == NULL) {\r
4000     hdc = GetDC(hwndMain);\r
4001     if (!appData.monoMode) {\r
4002       SelectPalette(hdc, hPal, FALSE);\r
4003       RealizePalette(hdc);\r
4004     }\r
4005     releaseDC = TRUE;\r
4006   } else {\r
4007     releaseDC = FALSE;\r
4008   }\r
4009 \r
4010 #if 0\r
4011   fprintf(debugFP, "*******************************\n"\r
4012                    "repaint = %s\n"\r
4013                    "dragInfo.from (%d,%d)\n"\r
4014                    "dragInfo.start (%d,%d)\n"\r
4015                    "dragInfo.pos (%d,%d)\n"\r
4016                    "dragInfo.lastpos (%d,%d)\n", \r
4017                     repaint ? "TRUE" : "FALSE",\r
4018                     dragInfo.from.x, dragInfo.from.y, \r
4019                     dragInfo.start.x, dragInfo.start.y,\r
4020                     dragInfo.pos.x, dragInfo.pos.y,\r
4021                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4022   fprintf(debugFP, "prev:  ");\r
4023   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4024     for (column = 0; column < BOARD_WIDTH; column++) {\r
4025       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4026     }\r
4027   }\r
4028   fprintf(debugFP, "\n");\r
4029   fprintf(debugFP, "board: ");\r
4030   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4031     for (column = 0; column < BOARD_WIDTH; column++) {\r
4032       fprintf(debugFP, "%d ", board[row][column]);\r
4033     }\r
4034   }\r
4035   fprintf(debugFP, "\n");\r
4036   fflush(debugFP);\r
4037 #endif\r
4038 \r
4039   /* Create some work-DCs */\r
4040   hdcmem = CreateCompatibleDC(hdc);\r
4041   tmphdc = CreateCompatibleDC(hdc);\r
4042 \r
4043   /* If dragging is in progress, we temporarely remove the piece */\r
4044   /* [HGM] or temporarily decrease count if stacked              */\r
4045   /*       !! Moved to before board compare !!                   */\r
4046   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4047     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4048     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4049             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4050         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4051     } else \r
4052     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4053             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4054         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4055     } else \r
4056         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4057   }\r
4058 \r
4059   /* Figure out which squares need updating by comparing the \r
4060    * newest board with the last drawn board and checking if\r
4061    * flipping has changed.\r
4062    */\r
4063   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4064     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4065       for (column = 0; column < BOARD_WIDTH; column++) {\r
4066         if (lastDrawn[row][column] != board[row][column]) {\r
4067           SquareToPos(row, column, &x, &y);\r
4068           clips[num_clips++] =\r
4069             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4070         }\r
4071       }\r
4072     }\r
4073     for (i=0; i<2; i++) {\r
4074       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4075           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4076         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4077             lastDrawnHighlight.sq[i].y >= 0) {\r
4078           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4079                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4080           clips[num_clips++] =\r
4081             CreateRectRgn(x - lineGap, y - lineGap, \r
4082                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4083         }\r
4084         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4085           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4086           clips[num_clips++] =\r
4087             CreateRectRgn(x - lineGap, y - lineGap, \r
4088                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4089         }\r
4090       }\r
4091     }\r
4092     for (i=0; i<2; i++) {\r
4093       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4094           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4095         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4096             lastDrawnPremove.sq[i].y >= 0) {\r
4097           SquareToPos(lastDrawnPremove.sq[i].y,\r
4098                       lastDrawnPremove.sq[i].x, &x, &y);\r
4099           clips[num_clips++] =\r
4100             CreateRectRgn(x - lineGap, y - lineGap, \r
4101                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4102         }\r
4103         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4104             premoveHighlightInfo.sq[i].y >= 0) {\r
4105           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4106                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4107           clips[num_clips++] =\r
4108             CreateRectRgn(x - lineGap, y - lineGap, \r
4109                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4110         }\r
4111       }\r
4112     }\r
4113   } else {\r
4114     fullrepaint = TRUE;\r
4115   }\r
4116 \r
4117   /* Create a buffer bitmap - this is the actual bitmap\r
4118    * being written to.  When all the work is done, we can\r
4119    * copy it to the real DC (the screen).  This avoids\r
4120    * the problems with flickering.\r
4121    */\r
4122   GetClientRect(hwndMain, &Rect);\r
4123   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4124                                         Rect.bottom-Rect.top+1);\r
4125   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4126   if (!appData.monoMode) {\r
4127     SelectPalette(hdcmem, hPal, FALSE);\r
4128   }\r
4129 \r
4130   /* Create clips for dragging */\r
4131   if (!fullrepaint) {\r
4132     if (dragInfo.from.x >= 0) {\r
4133       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4134       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4135     }\r
4136     if (dragInfo.start.x >= 0) {\r
4137       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4138       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4139     }\r
4140     if (dragInfo.pos.x >= 0) {\r
4141       x = dragInfo.pos.x - squareSize / 2;\r
4142       y = dragInfo.pos.y - squareSize / 2;\r
4143       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4144     }\r
4145     if (dragInfo.lastpos.x >= 0) {\r
4146       x = dragInfo.lastpos.x - squareSize / 2;\r
4147       y = dragInfo.lastpos.y - squareSize / 2;\r
4148       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4149     }\r
4150   }\r
4151 \r
4152   /* Are we animating a move?  \r
4153    * If so, \r
4154    *   - remove the piece from the board (temporarely)\r
4155    *   - calculate the clipping region\r
4156    */\r
4157   if (!fullrepaint) {\r
4158     if (animInfo.piece != EmptySquare) {\r
4159       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4160       x = boardRect.left + animInfo.lastpos.x;\r
4161       y = boardRect.top + animInfo.lastpos.y;\r
4162       x2 = boardRect.left + animInfo.pos.x;\r
4163       y2 = boardRect.top + animInfo.pos.y;\r
4164       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4165       /* Slight kludge.  The real problem is that after AnimateMove is\r
4166          done, the position on the screen does not match lastDrawn.\r
4167          This currently causes trouble only on e.p. captures in\r
4168          atomic, where the piece moves to an empty square and then\r
4169          explodes.  The old and new positions both had an empty square\r
4170          at the destination, but animation has drawn a piece there and\r
4171          we have to remember to erase it. */\r
4172       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4173     }\r
4174   }\r
4175 \r
4176   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4177   if (num_clips == 0)\r
4178     fullrepaint = TRUE;\r
4179 \r
4180   /* Set clipping on the memory DC */\r
4181   if (!fullrepaint) {\r
4182     SelectClipRgn(hdcmem, clips[0]);\r
4183     for (x = 1; x < num_clips; x++) {\r
4184       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4185         abort();  // this should never ever happen!\r
4186     }\r
4187   }\r
4188 \r
4189   /* Do all the drawing to the memory DC */\r
4190   DrawGridOnDC(hdcmem);\r
4191   DrawHighlightsOnDC(hdcmem);\r
4192   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4193 \r
4194   if( appData.highlightMoveWithArrow ) {\r
4195     DrawArrowHighlight(hdcmem);\r
4196   }\r
4197 \r
4198   DrawCoordsOnDC(hdcmem);\r
4199 \r
4200   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4201                  /* to make sure lastDrawn contains what is actually drawn */\r
4202 \r
4203   /* Put the dragged piece back into place and draw it (out of place!) */\r
4204     if (dragged_piece != EmptySquare) {\r
4205     /* [HGM] or restack */\r
4206     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4207                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4208     else\r
4209     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4210                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4211     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4212     x = dragInfo.pos.x - squareSize / 2;\r
4213     y = dragInfo.pos.y - squareSize / 2;\r
4214     DrawPieceOnDC(hdcmem, dragged_piece,\r
4215                   ((int) dragged_piece < (int) BlackPawn), \r
4216                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4217   }   \r
4218   \r
4219   /* Put the animated piece back into place and draw it */\r
4220   if (animInfo.piece != EmptySquare) {\r
4221     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4222     x = boardRect.left + animInfo.pos.x;\r
4223     y = boardRect.top + animInfo.pos.y;\r
4224     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4225                   ((int) animInfo.piece < (int) BlackPawn),\r
4226                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4227   }\r
4228 \r
4229   /* Release the bufferBitmap by selecting in the old bitmap \r
4230    * and delete the memory DC\r
4231    */\r
4232   SelectObject(hdcmem, oldBitmap);\r
4233   DeleteDC(hdcmem);\r
4234 \r
4235   /* Set clipping on the target DC */\r
4236   if (!fullrepaint) {\r
4237     SelectClipRgn(hdc, clips[0]);\r
4238     for (x = 1; x < num_clips; x++) {\r
4239       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4240         abort();   // this should never ever happen!\r
4241     } \r
4242   }\r
4243 \r
4244   /* Copy the new bitmap onto the screen in one go.\r
4245    * This way we avoid any flickering\r
4246    */\r
4247   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4248   BitBlt(hdc, boardRect.left, boardRect.top,\r
4249          boardRect.right - boardRect.left,\r
4250          boardRect.bottom - boardRect.top,\r
4251          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4252   SelectObject(tmphdc, oldBitmap);\r
4253 \r
4254   /* Massive cleanup */\r
4255   for (x = 0; x < num_clips; x++)\r
4256     DeleteObject(clips[x]);\r
4257 \r
4258   DeleteDC(tmphdc);\r
4259   DeleteObject(bufferBitmap);\r
4260 \r
4261   if (releaseDC) \r
4262     ReleaseDC(hwndMain, hdc);\r
4263   \r
4264   if (lastDrawnFlipView != flipView) {\r
4265     if (flipView)\r
4266       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4267     else\r
4268       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4269   }\r
4270 \r
4271 /*  CopyBoard(lastDrawn, board);*/\r
4272   lastDrawnHighlight = highlightInfo;\r
4273   lastDrawnPremove   = premoveHighlightInfo;\r
4274   lastDrawnFlipView = flipView;\r
4275   lastDrawnValid = 1;\r
4276 }\r
4277 \r
4278 \r
4279 /*---------------------------------------------------------------------------*\\r
4280 | CLIENT PAINT PROCEDURE\r
4281 |   This is the main event-handler for the WM_PAINT message.\r
4282 |\r
4283 \*---------------------------------------------------------------------------*/\r
4284 VOID\r
4285 PaintProc(HWND hwnd)\r
4286 {\r
4287   HDC         hdc;\r
4288   PAINTSTRUCT ps;\r
4289   HFONT       oldFont;\r
4290 \r
4291   if(hdc = BeginPaint(hwnd, &ps)) {\r
4292     if (IsIconic(hwnd)) {\r
4293       DrawIcon(hdc, 2, 2, iconCurrent);\r
4294     } else {\r
4295       if (!appData.monoMode) {\r
4296         SelectPalette(hdc, hPal, FALSE);\r
4297         RealizePalette(hdc);\r
4298       }\r
4299       HDCDrawPosition(hdc, 1, NULL);\r
4300       oldFont =\r
4301         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4302       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4303                  ETO_CLIPPED|ETO_OPAQUE,\r
4304                  &messageRect, messageText, strlen(messageText), NULL);\r
4305       SelectObject(hdc, oldFont);\r
4306       DisplayBothClocks();\r
4307     }\r
4308     EndPaint(hwnd,&ps);\r
4309   }\r
4310 \r
4311   return;\r
4312 }\r
4313 \r
4314 \r
4315 /*\r
4316  * If the user selects on a border boundary, return -1; if off the board,\r
4317  *   return -2.  Otherwise map the event coordinate to the square.\r
4318  * The offset boardRect.left or boardRect.top must already have been\r
4319  *   subtracted from x.\r
4320  */\r
4321 int\r
4322 EventToSquare(int x)\r
4323 {\r
4324   if (x <= 0)\r
4325     return -2;\r
4326   if (x < lineGap)\r
4327     return -1;\r
4328   x -= lineGap;\r
4329   if ((x % (squareSize + lineGap)) >= squareSize)\r
4330     return -1;\r
4331   x /= (squareSize + lineGap);\r
4332   if (x >= BOARD_SIZE)\r
4333     return -2;\r
4334   return x;\r
4335 }\r
4336 \r
4337 typedef struct {\r
4338   char piece;\r
4339   int command;\r
4340   char* name;\r
4341 } DropEnable;\r
4342 \r
4343 DropEnable dropEnables[] = {\r
4344   { 'P', DP_Pawn, "Pawn" },\r
4345   { 'N', DP_Knight, "Knight" },\r
4346   { 'B', DP_Bishop, "Bishop" },\r
4347   { 'R', DP_Rook, "Rook" },\r
4348   { 'Q', DP_Queen, "Queen" },\r
4349 };\r
4350 \r
4351 VOID\r
4352 SetupDropMenu(HMENU hmenu)\r
4353 {\r
4354   int i, count, enable;\r
4355   char *p;\r
4356   extern char white_holding[], black_holding[];\r
4357   char item[MSG_SIZ];\r
4358 \r
4359   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4360     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4361                dropEnables[i].piece);\r
4362     count = 0;\r
4363     while (p && *p++ == dropEnables[i].piece) count++;\r
4364     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4365     enable = count > 0 || !appData.testLegality\r
4366       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4367                       && !appData.icsActive);\r
4368     ModifyMenu(hmenu, dropEnables[i].command,\r
4369                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4370                dropEnables[i].command, item);\r
4371   }\r
4372 }\r
4373 \r
4374 static int fromX = -1, fromY = -1, toX, toY;\r
4375 \r
4376 /* Event handler for mouse messages */\r
4377 VOID\r
4378 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4379 {\r
4380   int x, y;\r
4381   POINT pt;\r
4382   static int recursive = 0;\r
4383   HMENU hmenu;\r
4384   BOOLEAN needsRedraw = FALSE;\r
4385   BOOLEAN saveAnimate;\r
4386   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4387   static BOOLEAN sameAgain = FALSE;\r
4388   ChessMove moveType;\r
4389 \r
4390   if (recursive) {\r
4391     if (message == WM_MBUTTONUP) {\r
4392       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4393          to the middle button: we simulate pressing the left button too!\r
4394          */\r
4395       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4396       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4397     }\r
4398     return;\r
4399   }\r
4400   recursive++;\r
4401   \r
4402   pt.x = LOWORD(lParam);\r
4403   pt.y = HIWORD(lParam);\r
4404   x = EventToSquare(pt.x - boardRect.left);\r
4405   y = EventToSquare(pt.y - boardRect.top);\r
4406   if (!flipView && y >= 0) {\r
4407     y = BOARD_HEIGHT - 1 - y;\r
4408   }\r
4409   if (flipView && x >= 0) {\r
4410     x = BOARD_WIDTH - 1 - x;\r
4411   }\r
4412 \r
4413   switch (message) {\r
4414   case WM_LBUTTONDOWN:\r
4415     ErrorPopDown();\r
4416     sameAgain = FALSE;\r
4417     if (y == -2) {\r
4418       /* Downclick vertically off board; check if on clock */\r
4419       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4420         if (gameMode == EditPosition) {\r
4421           SetWhiteToPlayEvent();\r
4422         } else if (gameMode == IcsPlayingBlack ||\r
4423                    gameMode == MachinePlaysWhite) {\r
4424           CallFlagEvent();\r
4425         } else if (gameMode == EditGame) {\r
4426           AdjustClock(flipClock, -1);\r
4427         }\r
4428       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4429         if (gameMode == EditPosition) {\r
4430           SetBlackToPlayEvent();\r
4431         } else if (gameMode == IcsPlayingWhite ||\r
4432                    gameMode == MachinePlaysBlack) {\r
4433           CallFlagEvent();\r
4434         } else if (gameMode == EditGame) {\r
4435           AdjustClock(!flipClock, -1);\r
4436         }\r
4437       }\r
4438       if (!appData.highlightLastMove) {\r
4439         ClearHighlights();\r
4440         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4441       }\r
4442       fromX = fromY = -1;\r
4443       dragInfo.start.x = dragInfo.start.y = -1;\r
4444       dragInfo.from = dragInfo.start;\r
4445       break;\r
4446     } else if (x < 0 || y < 0\r
4447       /* [HGM] block clicks between board and holdings */\r
4448               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4449               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4450               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4451         /* EditPosition, empty square, or different color piece;\r
4452            click-click move is possible */\r
4453                                ) {\r
4454       break;\r
4455     } else if (fromX == x && fromY == y) {\r
4456       /* Downclick on same square again */\r
4457       ClearHighlights();\r
4458       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4459       sameAgain = TRUE;  \r
4460     } else if (fromX != -1 &&\r
4461                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4462                                                                         ) {\r
4463       /* Downclick on different square. */\r
4464       /* [HGM] if on holdings file, should count as new first click ! */\r
4465       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4466         toX = x;\r
4467         toY = y;\r
4468         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4469            to make sure move is legal before showing promotion popup */\r
4470         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4471         if(moveType != ImpossibleMove) {\r
4472           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4473           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4474              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4475               appData.alwaysPromoteToQueen) {\r
4476                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4477                   if (!appData.highlightLastMove) {\r
4478                       ClearHighlights();\r
4479                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4480                   }\r
4481           } else\r
4482           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4483                   SetHighlights(fromX, fromY, toX, toY);\r
4484                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4485                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4486                      If promotion to Q is legal, all are legal! */\r
4487                   PromotionPopup(hwnd);\r
4488           } else {       /* not a promotion */\r
4489              if (appData.animate || appData.highlightLastMove) {\r
4490                  SetHighlights(fromX, fromY, toX, toY);\r
4491              } else {\r
4492                  ClearHighlights();\r
4493              }\r
4494              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4495              if (appData.animate && !appData.highlightLastMove) {\r
4496                   ClearHighlights();\r
4497                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4498              }\r
4499           }\r
4500           break;\r
4501         }\r
4502         if (gotPremove) {\r
4503             /* [HGM] it seemed that braces were missing here */\r
4504             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4505             fromX = fromY = -1;\r
4506             break;\r
4507         }\r
4508       }\r
4509       ClearHighlights();\r
4510       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4511     }\r
4512     /* First downclick, or restart on a square with same color piece */\r
4513     if (!frozen && OKToStartUserMove(x, y)) {\r
4514       fromX = x;\r
4515       fromY = y;\r
4516       dragInfo.lastpos = pt;\r
4517       dragInfo.from.x = fromX;\r
4518       dragInfo.from.y = fromY;\r
4519       dragInfo.start = dragInfo.from;\r
4520       SetCapture(hwndMain);\r
4521     } else {\r
4522       fromX = fromY = -1;\r
4523       dragInfo.start.x = dragInfo.start.y = -1;\r
4524       dragInfo.from = dragInfo.start;\r
4525       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4526     }\r
4527     break;\r
4528 \r
4529   case WM_LBUTTONUP:\r
4530     ReleaseCapture();\r
4531     if (fromX == -1) break;\r
4532     if (x == fromX && y == fromY) {\r
4533       dragInfo.from.x = dragInfo.from.y = -1;\r
4534       /* Upclick on same square */\r
4535       if (sameAgain) {\r
4536         /* Clicked same square twice: abort click-click move */\r
4537         fromX = fromY = -1;\r
4538         gotPremove = 0;\r
4539         ClearPremoveHighlights();\r
4540       } else {\r
4541         /* First square clicked: start click-click move */\r
4542         SetHighlights(fromX, fromY, -1, -1);\r
4543       }\r
4544       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4545     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4546       /* Errant click; ignore */\r
4547       break;\r
4548     } else {\r
4549       /* Finish drag move. */\r
4550     if (appData.debugMode) {\r
4551         fprintf(debugFP, "release\n");\r
4552     }\r
4553       dragInfo.from.x = dragInfo.from.y = -1;\r
4554       toX = x;\r
4555       toY = y;\r
4556       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4557       appData.animate = appData.animate && !appData.animateDragging;\r
4558       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4559       if(moveType != ImpossibleMove) {\r
4560           /* [HGM] use move type to determine if move is promotion.\r
4561              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4562           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4563              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4564               appData.alwaysPromoteToQueen) \r
4565                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4566           else \r
4567           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4568                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4569                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4570         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4571       }\r
4572       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4573       appData.animate = saveAnimate;\r
4574       fromX = fromY = -1;\r
4575       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4576         ClearHighlights();\r
4577       }\r
4578       if (appData.animate || appData.animateDragging ||\r
4579           appData.highlightDragging || gotPremove) {\r
4580         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4581       }\r
4582     }\r
4583     dragInfo.start.x = dragInfo.start.y = -1; \r
4584     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4585     break;\r
4586 \r
4587   case WM_MOUSEMOVE:\r
4588     if ((appData.animateDragging || appData.highlightDragging)\r
4589         && (wParam & MK_LBUTTON)\r
4590         && dragInfo.from.x >= 0) \r
4591     {\r
4592       BOOL full_repaint = FALSE;\r
4593 \r
4594       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4595       if (appData.animateDragging) {\r
4596         dragInfo.pos = pt;\r
4597       }\r
4598       if (appData.highlightDragging) {\r
4599         SetHighlights(fromX, fromY, x, y);\r
4600         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4601             full_repaint = TRUE;\r
4602         }\r
4603       }\r
4604       \r
4605       DrawPosition( full_repaint, NULL);\r
4606       \r
4607       dragInfo.lastpos = dragInfo.pos;\r
4608     }\r
4609     break;\r
4610 \r
4611   case WM_MBUTTONDOWN:\r
4612   case WM_RBUTTONDOWN:\r
4613     ErrorPopDown();\r
4614     ReleaseCapture();\r
4615     fromX = fromY = -1;\r
4616     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4617     dragInfo.start.x = dragInfo.start.y = -1;\r
4618     dragInfo.from = dragInfo.start;\r
4619     dragInfo.lastpos = dragInfo.pos;\r
4620     if (appData.highlightDragging) {\r
4621       ClearHighlights();\r
4622     }\r
4623     if(y == -2) {\r
4624       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4625       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4626           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4627       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4628           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4629       }\r
4630     }\r
4631     DrawPosition(TRUE, NULL);\r
4632 \r
4633     switch (gameMode) {\r
4634     case EditPosition:\r
4635     case IcsExamining:\r
4636       if (x < 0 || y < 0) break;\r
4637       fromX = x;\r
4638       fromY = y;\r
4639       if (message == WM_MBUTTONDOWN) {\r
4640         buttonCount = 3;  /* even if system didn't think so */\r
4641         if (wParam & MK_SHIFT) \r
4642           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4643         else\r
4644           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4645       } else { /* message == WM_RBUTTONDOWN */\r
4646 #if 0\r
4647         if (buttonCount == 3) {\r
4648           if (wParam & MK_SHIFT) \r
4649             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4650           else\r
4651             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4652         } else {\r
4653           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4654         }\r
4655 #else\r
4656         /* Just have one menu, on the right button.  Windows users don't\r
4657            think to try the middle one, and sometimes other software steals\r
4658            it, or it doesn't really exist. */\r
4659         if(gameInfo.variant != VariantShogi)\r
4660             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4661         else\r
4662             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4663 #endif\r
4664       }\r
4665       break;\r
4666     case IcsPlayingWhite:\r
4667     case IcsPlayingBlack:\r
4668     case EditGame:\r
4669     case MachinePlaysWhite:\r
4670     case MachinePlaysBlack:\r
4671       if (appData.testLegality &&\r
4672           gameInfo.variant != VariantBughouse &&\r
4673           gameInfo.variant != VariantCrazyhouse) break;\r
4674       if (x < 0 || y < 0) break;\r
4675       fromX = x;\r
4676       fromY = y;\r
4677       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4678       SetupDropMenu(hmenu);\r
4679       MenuPopup(hwnd, pt, hmenu, -1);\r
4680       break;\r
4681     default:\r
4682       break;\r
4683     }\r
4684     break;\r
4685   }\r
4686 \r
4687   recursive--;\r
4688 }\r
4689 \r
4690 /* Preprocess messages for buttons in main window */\r
4691 LRESULT CALLBACK\r
4692 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4693 {\r
4694   int id = GetWindowLong(hwnd, GWL_ID);\r
4695   int i, dir;\r
4696 \r
4697   for (i=0; i<N_BUTTONS; i++) {\r
4698     if (buttonDesc[i].id == id) break;\r
4699   }\r
4700   if (i == N_BUTTONS) return 0;\r
4701   switch (message) {\r
4702   case WM_KEYDOWN:\r
4703     switch (wParam) {\r
4704     case VK_LEFT:\r
4705     case VK_RIGHT:\r
4706       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4707       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4708       return TRUE;\r
4709     }\r
4710     break;\r
4711   case WM_CHAR:\r
4712     switch (wParam) {\r
4713     case '\r':\r
4714       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4715       return TRUE;\r
4716     case '\t':\r
4717       if (appData.icsActive) {\r
4718         if (GetKeyState(VK_SHIFT) < 0) {\r
4719           /* shifted */\r
4720           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4721           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4722           SetFocus(h);\r
4723         } else {\r
4724           /* unshifted */\r
4725           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4726           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4727           SetFocus(h);\r
4728         }\r
4729         return TRUE;\r
4730       }\r
4731       break;\r
4732     default:\r
4733       if (appData.icsActive) {\r
4734         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4735         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4736         SetFocus(h);\r
4737         SendMessage(h, WM_CHAR, wParam, lParam);\r
4738         return TRUE;\r
4739       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4740         PopUpMoveDialog((char)wParam);\r
4741       }\r
4742       break;\r
4743     }\r
4744     break;\r
4745   }\r
4746   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4747 }\r
4748 \r
4749 /* Process messages for Promotion dialog box */\r
4750 LRESULT CALLBACK\r
4751 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4752 {\r
4753   char promoChar;\r
4754 \r
4755   switch (message) {\r
4756   case WM_INITDIALOG: /* message: initialize dialog box */\r
4757     /* Center the dialog over the application window */\r
4758     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4759     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4760       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4761        gameInfo.variant == VariantGiveaway) ?\r
4762                SW_SHOW : SW_HIDE);\r
4763     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4764     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4765        (PieceToChar(WhiteCardinal) >= 'A' &&\r
4766         PieceToChar(WhiteCardinal) != '~' ||\r
4767         PieceToChar(BlackCardinal) >= 'A' &&\r
4768         PieceToChar(BlackCardinal) != '~'   ) ?\r
4769                SW_SHOW : SW_HIDE);\r
4770     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4771        (PieceToChar(WhiteMarshall) >= 'A' &&\r
4772         PieceToChar(WhiteMarshall) != '~' ||\r
4773         PieceToChar(BlackMarshall) >= 'A' &&\r
4774         PieceToChar(BlackMarshall) != '~'   ) ?\r
4775                SW_SHOW : SW_HIDE);\r
4776     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4777     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4778        gameInfo.variant != VariantShogi ?\r
4779                SW_SHOW : SW_HIDE);\r
4780     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4781        gameInfo.variant != VariantShogi ?\r
4782                SW_SHOW : SW_HIDE);\r
4783     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4784        gameInfo.variant == VariantShogi ?\r
4785                SW_SHOW : SW_HIDE);\r
4786     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4787        gameInfo.variant == VariantShogi ?\r
4788                SW_SHOW : SW_HIDE);\r
4789     return TRUE;\r
4790 \r
4791   case WM_COMMAND: /* message: received a command */\r
4792     switch (LOWORD(wParam)) {\r
4793     case IDCANCEL:\r
4794       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4795       ClearHighlights();\r
4796       DrawPosition(FALSE, NULL);\r
4797       return TRUE;\r
4798     case PB_King:\r
4799       promoChar = PieceToChar(BlackKing);\r
4800       break;\r
4801     case PB_Queen:\r
4802       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4803       break;\r
4804     case PB_Rook:\r
4805       promoChar = PieceToChar(BlackRook);\r
4806       break;\r
4807     case PB_Bishop:\r
4808       promoChar = PieceToChar(BlackBishop);\r
4809       break;\r
4810     case PB_Chancellor:\r
4811       promoChar = PieceToChar(BlackMarshall);\r
4812       break;\r
4813     case PB_Archbishop:\r
4814       promoChar = PieceToChar(BlackCardinal);\r
4815       break;\r
4816     case PB_Knight:\r
4817       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4818       break;\r
4819     default:\r
4820       return FALSE;\r
4821     }\r
4822     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4823     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4824        only show the popup when we are already sure the move is valid or\r
4825        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4826        will figure out it is a promotion from the promoChar. */\r
4827     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
4828     if (!appData.highlightLastMove) {\r
4829       ClearHighlights();\r
4830       DrawPosition(FALSE, NULL);\r
4831     }\r
4832     return TRUE;\r
4833   }\r
4834   return FALSE;\r
4835 }\r
4836 \r
4837 /* Pop up promotion dialog */\r
4838 VOID\r
4839 PromotionPopup(HWND hwnd)\r
4840 {\r
4841   FARPROC lpProc;\r
4842 \r
4843   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4844   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4845     hwnd, (DLGPROC)lpProc);\r
4846   FreeProcInstance(lpProc);\r
4847 }\r
4848 \r
4849 /* Toggle ShowThinking */\r
4850 VOID\r
4851 ToggleShowThinking()\r
4852 {\r
4853   ShowThinkingEvent(!appData.showThinking);\r
4854 }\r
4855 \r
4856 VOID\r
4857 LoadGameDialog(HWND hwnd, char* title)\r
4858 {\r
4859   UINT number = 0;\r
4860   FILE *f;\r
4861   char fileTitle[MSG_SIZ];\r
4862   f = OpenFileDialog(hwnd, FALSE, "",\r
4863                      appData.oldSaveStyle ? "gam" : "pgn",\r
4864                      GAME_FILT,\r
4865                      title, &number, fileTitle, NULL);\r
4866   if (f != NULL) {\r
4867     cmailMsgLoaded = FALSE;\r
4868     if (number == 0) {\r
4869       int error = GameListBuild(f);\r
4870       if (error) {\r
4871         DisplayError("Cannot build game list", error);\r
4872       } else if (!ListEmpty(&gameList) &&\r
4873                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4874         GameListPopUp(f, fileTitle);\r
4875         return;\r
4876       }\r
4877       GameListDestroy();\r
4878       number = 1;\r
4879     }\r
4880     LoadGame(f, number, fileTitle, FALSE);\r
4881   }\r
4882 }\r
4883 \r
4884 VOID\r
4885 ChangedConsoleFont()\r
4886 {\r
4887   CHARFORMAT cfmt;\r
4888   CHARRANGE tmpsel, sel;\r
4889   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4890   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4891   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4892   PARAFORMAT paraf;\r
4893 \r
4894   cfmt.cbSize = sizeof(CHARFORMAT);\r
4895   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4896   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4897   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4898    * size.  This was undocumented in the version of MSVC++ that I had\r
4899    * when I wrote the code, but is apparently documented now.\r
4900    */\r
4901   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4902   cfmt.bCharSet = f->lf.lfCharSet;\r
4903   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4904   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4905   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4906   /* Why are the following seemingly needed too? */\r
4907   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4908   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4909   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4910   tmpsel.cpMin = 0;\r
4911   tmpsel.cpMax = -1; /*999999?*/\r
4912   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4913   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4914   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4915    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4916    */\r
4917   paraf.cbSize = sizeof(paraf);\r
4918   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4919   paraf.dxStartIndent = 0;\r
4920   paraf.dxOffset = WRAP_INDENT;\r
4921   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4922   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4923 }\r
4924 \r
4925 /*---------------------------------------------------------------------------*\\r
4926  *\r
4927  * Window Proc for main window\r
4928  *\r
4929 \*---------------------------------------------------------------------------*/\r
4930 \r
4931 /* Process messages for main window, etc. */\r
4932 LRESULT CALLBACK\r
4933 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4934 {\r
4935   FARPROC lpProc;\r
4936   int wmId, wmEvent;\r
4937   char *defName;\r
4938   FILE *f;\r
4939   UINT number;\r
4940   char fileTitle[MSG_SIZ];\r
4941   static SnapData sd;\r
4942 \r
4943   switch (message) {\r
4944 \r
4945   case WM_PAINT: /* message: repaint portion of window */\r
4946     PaintProc(hwnd);\r
4947     break;\r
4948 \r
4949   case WM_ERASEBKGND:\r
4950     if (IsIconic(hwnd)) {\r
4951       /* Cheat; change the message */\r
4952       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4953     } else {\r
4954       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4955     }\r
4956     break;\r
4957 \r
4958   case WM_LBUTTONDOWN:\r
4959   case WM_MBUTTONDOWN:\r
4960   case WM_RBUTTONDOWN:\r
4961   case WM_LBUTTONUP:\r
4962   case WM_MBUTTONUP:\r
4963   case WM_RBUTTONUP:\r
4964   case WM_MOUSEMOVE:\r
4965     MouseEvent(hwnd, message, wParam, lParam);\r
4966     break;\r
4967 \r
4968   case WM_CHAR:\r
4969     \r
4970     if (appData.icsActive) {\r
4971       if (wParam == '\t') {\r
4972         if (GetKeyState(VK_SHIFT) < 0) {\r
4973           /* shifted */\r
4974           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4975           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4976           SetFocus(h);\r
4977         } else {\r
4978           /* unshifted */\r
4979           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4980           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4981           SetFocus(h);\r
4982         }\r
4983       } else {\r
4984         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4985         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4986         SetFocus(h);\r
4987         SendMessage(h, message, wParam, lParam);\r
4988       }\r
4989     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4990       PopUpMoveDialog((char)wParam);\r
4991     }\r
4992     break;\r
4993 \r
4994   case WM_PALETTECHANGED:\r
4995     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4996       int nnew;\r
4997       HDC hdc = GetDC(hwndMain);\r
4998       SelectPalette(hdc, hPal, TRUE);\r
4999       nnew = RealizePalette(hdc);\r
5000       if (nnew > 0) {\r
5001         paletteChanged = TRUE;\r
5002 #if 0\r
5003         UpdateColors(hdc);\r
5004 #else\r
5005         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5006 #endif\r
5007       }\r
5008       ReleaseDC(hwnd, hdc);\r
5009     }\r
5010     break;\r
5011 \r
5012   case WM_QUERYNEWPALETTE:\r
5013     if (!appData.monoMode /*&& paletteChanged*/) {\r
5014       int nnew;\r
5015       HDC hdc = GetDC(hwndMain);\r
5016       paletteChanged = FALSE;\r
5017       SelectPalette(hdc, hPal, FALSE);\r
5018       nnew = RealizePalette(hdc);\r
5019       if (nnew > 0) {\r
5020         InvalidateRect(hwnd, &boardRect, FALSE);\r
5021       }\r
5022       ReleaseDC(hwnd, hdc);\r
5023       return TRUE;\r
5024     }\r
5025     return FALSE;\r
5026 \r
5027   case WM_COMMAND: /* message: command from application menu */\r
5028     wmId    = LOWORD(wParam);\r
5029     wmEvent = HIWORD(wParam);\r
5030 \r
5031     switch (wmId) {\r
5032     case IDM_NewGame:\r
5033       ResetGameEvent();\r
5034       AnalysisPopDown();\r
5035       break;\r
5036 \r
5037     case IDM_NewGameFRC:\r
5038       if( NewGameFRC() == 0 ) {\r
5039         ResetGameEvent();\r
5040         AnalysisPopDown();\r
5041       }\r
5042       break;\r
5043 \r
5044     case IDM_NewVariant:\r
5045       NewVariantPopup(hwnd);\r
5046       break;\r
5047 \r
5048     case IDM_LoadGame:\r
5049       LoadGameDialog(hwnd, "Load Game from File");\r
5050       break;\r
5051 \r
5052     case IDM_LoadNextGame:\r
5053       ReloadGame(1);\r
5054       break;\r
5055 \r
5056     case IDM_LoadPrevGame:\r
5057       ReloadGame(-1);\r
5058       break;\r
5059 \r
5060     case IDM_ReloadGame:\r
5061       ReloadGame(0);\r
5062       break;\r
5063 \r
5064     case IDM_LoadPosition:\r
5065       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5066         Reset(FALSE, TRUE);\r
5067       }\r
5068       number = 1;\r
5069       f = OpenFileDialog(hwnd, FALSE, "",\r
5070                          appData.oldSaveStyle ? "pos" : "fen",\r
5071                          POSITION_FILT,\r
5072                          "Load Position from File", &number, fileTitle, NULL);\r
5073       if (f != NULL) {\r
5074         LoadPosition(f, number, fileTitle);\r
5075       }\r
5076       break;\r
5077 \r
5078     case IDM_LoadNextPosition:\r
5079       ReloadPosition(1);\r
5080       break;\r
5081 \r
5082     case IDM_LoadPrevPosition:\r
5083       ReloadPosition(-1);\r
5084       break;\r
5085 \r
5086     case IDM_ReloadPosition:\r
5087       ReloadPosition(0);\r
5088       break;\r
5089 \r
5090     case IDM_SaveGame:\r
5091       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5092       f = OpenFileDialog(hwnd, TRUE, defName,\r
5093                          appData.oldSaveStyle ? "gam" : "pgn",\r
5094                          GAME_FILT,\r
5095                          "Save Game to File", NULL, fileTitle, NULL);\r
5096       if (f != NULL) {\r
5097         SaveGame(f, 0, "");\r
5098       }\r
5099       break;\r
5100 \r
5101     case IDM_SavePosition:\r
5102       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5103       f = OpenFileDialog(hwnd, TRUE, defName,\r
5104                          appData.oldSaveStyle ? "pos" : "fen",\r
5105                          POSITION_FILT,\r
5106                          "Save Position to File", NULL, fileTitle, NULL);\r
5107       if (f != NULL) {\r
5108         SavePosition(f, 0, "");\r
5109       }\r
5110       break;\r
5111 \r
5112     case IDM_CopyGame:\r
5113       CopyGameToClipboard();\r
5114       break;\r
5115 \r
5116     case IDM_PasteGame:\r
5117       PasteGameFromClipboard();\r
5118       break;\r
5119 \r
5120     case IDM_CopyGameListToClipboard:\r
5121       CopyGameListToClipboard();\r
5122       break;\r
5123 \r
5124     /* [AS] Autodetect FEN or PGN data */\r
5125     case IDM_PasteAny:\r
5126       PasteGameOrFENFromClipboard();\r
5127       break;\r
5128 \r
5129     /* [AS] Move history */\r
5130     case IDM_ShowMoveHistory:\r
5131         if( MoveHistoryIsUp() ) {\r
5132             MoveHistoryPopDown();\r
5133         }\r
5134         else {\r
5135             MoveHistoryPopUp();\r
5136         }\r
5137         break;\r
5138 \r
5139     /* [AS] Eval graph */\r
5140     case IDM_ShowEvalGraph:\r
5141         if( EvalGraphIsUp() ) {\r
5142             EvalGraphPopDown();\r
5143         }\r
5144         else {\r
5145             EvalGraphPopUp();\r
5146         }\r
5147         break;\r
5148 \r
5149     /* [AS] Engine output */\r
5150     case IDM_ShowEngineOutput:\r
5151         if( EngineOutputIsUp() ) {\r
5152             EngineOutputPopDown();\r
5153         }\r
5154         else {\r
5155             EngineOutputPopUp();\r
5156         }\r
5157         break;\r
5158 \r
5159     /* [AS] User adjudication */\r
5160     case IDM_UserAdjudication_White:\r
5161         UserAdjudicationEvent( +1 );\r
5162         break;\r
5163 \r
5164     case IDM_UserAdjudication_Black:\r
5165         UserAdjudicationEvent( -1 );\r
5166         break;\r
5167 \r
5168     case IDM_UserAdjudication_Draw:\r
5169         UserAdjudicationEvent( 0 );\r
5170         break;\r
5171 \r
5172     /* [AS] Game list options dialog */\r
5173     case IDM_GameListOptions:\r
5174       GameListOptions();\r
5175       break;\r
5176 \r
5177     case IDM_CopyPosition:\r
5178       CopyFENToClipboard();\r
5179       break;\r
5180 \r
5181     case IDM_PastePosition:\r
5182       PasteFENFromClipboard();\r
5183       break;\r
5184 \r
5185     case IDM_MailMove:\r
5186       MailMoveEvent();\r
5187       break;\r
5188 \r
5189     case IDM_ReloadCMailMsg:\r
5190       Reset(TRUE, TRUE);\r
5191       ReloadCmailMsgEvent(FALSE);\r
5192       break;\r
5193 \r
5194     case IDM_Minimize:\r
5195       ShowWindow(hwnd, SW_MINIMIZE);\r
5196       break;\r
5197 \r
5198     case IDM_Exit:\r
5199       ExitEvent(0);\r
5200       break;\r
5201 \r
5202     case IDM_MachineWhite:\r
5203       MachineWhiteEvent();\r
5204       /*\r
5205        * refresh the tags dialog only if it's visible\r
5206        */\r
5207       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5208           char *tags;\r
5209           tags = PGNTags(&gameInfo);\r
5210           TagsPopUp(tags, CmailMsg());\r
5211           free(tags);\r
5212       }\r
5213       break;\r
5214 \r
5215     case IDM_MachineBlack:\r
5216       MachineBlackEvent();\r
5217       /*\r
5218        * refresh the tags dialog only if it's visible\r
5219        */\r
5220       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5221           char *tags;\r
5222           tags = PGNTags(&gameInfo);\r
5223           TagsPopUp(tags, CmailMsg());\r
5224           free(tags);\r
5225       }\r
5226       break;\r
5227 \r
5228     case IDM_TwoMachines:\r
5229       TwoMachinesEvent();\r
5230       /*\r
5231        * refresh the tags dialog only if it's visible\r
5232        */\r
5233       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5234           char *tags;\r
5235           tags = PGNTags(&gameInfo);\r
5236           TagsPopUp(tags, CmailMsg());\r
5237           free(tags);\r
5238       }\r
5239       break;\r
5240 \r
5241     case IDM_AnalysisMode:\r
5242       if (!first.analysisSupport) {\r
5243         char buf[MSG_SIZ];\r
5244         sprintf(buf, "%s does not support analysis", first.tidy);\r
5245         DisplayError(buf, 0);\r
5246       } else {\r
5247         if (!appData.showThinking) ToggleShowThinking();\r
5248         AnalyzeModeEvent();\r
5249       }\r
5250       break;\r
5251 \r
5252     case IDM_AnalyzeFile:\r
5253       if (!first.analysisSupport) {\r
5254         char buf[MSG_SIZ];\r
5255         sprintf(buf, "%s does not support analysis", first.tidy);\r
5256         DisplayError(buf, 0);\r
5257       } else {\r
5258         if (!appData.showThinking) ToggleShowThinking();\r
5259         AnalyzeFileEvent();\r
5260         LoadGameDialog(hwnd, "Analyze Game from File");\r
5261         AnalysisPeriodicEvent(1);\r
5262       }\r
5263       break;\r
5264 \r
5265     case IDM_IcsClient:\r
5266       IcsClientEvent();\r
5267       break;\r
5268 \r
5269     case IDM_EditGame:\r
5270       EditGameEvent();\r
5271       break;\r
5272 \r
5273     case IDM_EditPosition:\r
5274       EditPositionEvent();\r
5275       break;\r
5276 \r
5277     case IDM_Training:\r
5278       TrainingEvent();\r
5279       break;\r
5280 \r
5281     case IDM_ShowGameList:\r
5282       ShowGameListProc();\r
5283       break;\r
5284 \r
5285     case IDM_EditTags:\r
5286       EditTagsProc();\r
5287       break;\r
5288 \r
5289     case IDM_EditComment:\r
5290       if (commentDialogUp && editComment) {\r
5291         CommentPopDown();\r
5292       } else {\r
5293         EditCommentEvent();\r
5294       }\r
5295       break;\r
5296 \r
5297     case IDM_Pause:\r
5298       PauseEvent();\r
5299       break;\r
5300 \r
5301     case IDM_Accept:\r
5302       AcceptEvent();\r
5303       break;\r
5304 \r
5305     case IDM_Decline:\r
5306       DeclineEvent();\r
5307       break;\r
5308 \r
5309     case IDM_Rematch:\r
5310       RematchEvent();\r
5311       break;\r
5312 \r
5313     case IDM_CallFlag:\r
5314       CallFlagEvent();\r
5315       break;\r
5316 \r
5317     case IDM_Draw:\r
5318       DrawEvent();\r
5319       break;\r
5320 \r
5321     case IDM_Adjourn:\r
5322       AdjournEvent();\r
5323       break;\r
5324 \r
5325     case IDM_Abort:\r
5326       AbortEvent();\r
5327       break;\r
5328 \r
5329     case IDM_Resign:\r
5330       ResignEvent();\r
5331       break;\r
5332 \r
5333     case IDM_StopObserving:\r
5334       StopObservingEvent();\r
5335       break;\r
5336 \r
5337     case IDM_StopExamining:\r
5338       StopExaminingEvent();\r
5339       break;\r
5340 \r
5341     case IDM_TypeInMove:\r
5342       PopUpMoveDialog('\000');\r
5343       break;\r
5344 \r
5345     case IDM_Backward:\r
5346       BackwardEvent();\r
5347       SetFocus(hwndMain);\r
5348       break;\r
5349 \r
5350     case IDM_Forward:\r
5351       ForwardEvent();\r
5352       SetFocus(hwndMain);\r
5353       break;\r
5354 \r
5355     case IDM_ToStart:\r
5356       ToStartEvent();\r
5357       SetFocus(hwndMain);\r
5358       break;\r
5359 \r
5360     case IDM_ToEnd:\r
5361       ToEndEvent();\r
5362       SetFocus(hwndMain);\r
5363       break;\r
5364 \r
5365     case IDM_Revert:\r
5366       RevertEvent();\r
5367       break;\r
5368 \r
5369     case IDM_TruncateGame:\r
5370       TruncateGameEvent();\r
5371       break;\r
5372 \r
5373     case IDM_MoveNow:\r
5374       MoveNowEvent();\r
5375       break;\r
5376 \r
5377     case IDM_RetractMove:\r
5378       RetractMoveEvent();\r
5379       break;\r
5380 \r
5381     case IDM_FlipView:\r
5382       flipView = !flipView;\r
5383       DrawPosition(FALSE, NULL);\r
5384       break;\r
5385 \r
5386     case IDM_FlipClock:\r
5387       flipClock = !flipClock;\r
5388       DisplayBothClocks();\r
5389       break;\r
5390 \r
5391     case IDM_GeneralOptions:\r
5392       GeneralOptionsPopup(hwnd);\r
5393       DrawPosition(TRUE, NULL);\r
5394       break;\r
5395 \r
5396     case IDM_BoardOptions:\r
5397       BoardOptionsPopup(hwnd);\r
5398       break;\r
5399 \r
5400     case IDM_EnginePlayOptions:\r
5401       EnginePlayOptionsPopup(hwnd);\r
5402       break;\r
5403 \r
5404     case IDM_OptionsUCI:\r
5405       UciOptionsPopup(hwnd);\r
5406       break;\r
5407 \r
5408     case IDM_IcsOptions:\r
5409       IcsOptionsPopup(hwnd);\r
5410       break;\r
5411 \r
5412     case IDM_Fonts:\r
5413       FontsOptionsPopup(hwnd);\r
5414       break;\r
5415 \r
5416     case IDM_Sounds:\r
5417       SoundOptionsPopup(hwnd);\r
5418       break;\r
5419 \r
5420     case IDM_CommPort:\r
5421       CommPortOptionsPopup(hwnd);\r
5422       break;\r
5423 \r
5424     case IDM_LoadOptions:\r
5425       LoadOptionsPopup(hwnd);\r
5426       break;\r
5427 \r
5428     case IDM_SaveOptions:\r
5429       SaveOptionsPopup(hwnd);\r
5430       break;\r
5431 \r
5432     case IDM_TimeControl:\r
5433       TimeControlOptionsPopup(hwnd);\r
5434       break;\r
5435 \r
5436     case IDM_SaveSettings:\r
5437       SaveSettings(settingsFileName);\r
5438       break;\r
5439 \r
5440     case IDM_SaveSettingsOnExit:\r
5441       saveSettingsOnExit = !saveSettingsOnExit;\r
5442       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5443                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5444                                          MF_CHECKED : MF_UNCHECKED));\r
5445       break;\r
5446 \r
5447     case IDM_Hint:\r
5448       HintEvent();\r
5449       break;\r
5450 \r
5451     case IDM_Book:\r
5452       BookEvent();\r
5453       break;\r
5454 \r
5455     case IDM_AboutGame:\r
5456       AboutGameEvent();\r
5457       break;\r
5458 \r
5459     case IDM_Debug:\r
5460       appData.debugMode = !appData.debugMode;\r
5461       if (appData.debugMode) {\r
5462         char dir[MSG_SIZ];\r
5463         GetCurrentDirectory(MSG_SIZ, dir);\r
5464         SetCurrentDirectory(installDir);\r
5465         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5466         SetCurrentDirectory(dir);\r
5467         setbuf(debugFP, NULL);\r
5468       } else {\r
5469         fclose(debugFP);\r
5470         debugFP = NULL;\r
5471       }\r
5472       break;\r
5473 \r
5474     case IDM_HELPCONTENTS:\r
5475       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5476         MessageBox (GetFocus(),\r
5477                     "Unable to activate help",\r
5478                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5479       }\r
5480       break;\r
5481 \r
5482     case IDM_HELPSEARCH:\r
5483       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5484         MessageBox (GetFocus(),\r
5485                     "Unable to activate help",\r
5486                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5487       }\r
5488       break;\r
5489 \r
5490     case IDM_HELPHELP:\r
5491       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5492         MessageBox (GetFocus(),\r
5493                     "Unable to activate help",\r
5494                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5495       }\r
5496       break;\r
5497 \r
5498     case IDM_ABOUT:\r
5499       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5500       DialogBox(hInst, \r
5501         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5502         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5503       FreeProcInstance(lpProc);\r
5504       break;\r
5505 \r
5506     case IDM_DirectCommand1:\r
5507       AskQuestionEvent("Direct Command",\r
5508                        "Send to chess program:", "", "1");\r
5509       break;\r
5510     case IDM_DirectCommand2:\r
5511       AskQuestionEvent("Direct Command",\r
5512                        "Send to second chess program:", "", "2");\r
5513       break;\r
5514 \r
5515     case EP_WhitePawn:\r
5516       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_WhiteKnight:\r
5521       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_WhiteBishop:\r
5526       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_WhiteRook:\r
5531       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_WhiteQueen:\r
5536       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_WhiteFerz:\r
5541       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_WhiteWazir:\r
5546       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_WhiteAlfil:\r
5551       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_WhiteCannon:\r
5556       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_WhiteCardinal:\r
5561       EditPositionMenuEvent(WhiteCardinal, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_WhiteMarshall:\r
5566       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_WhiteKing:\r
5571       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_BlackPawn:\r
5576       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_BlackKnight:\r
5581       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case EP_BlackBishop:\r
5586       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case EP_BlackRook:\r
5591       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case EP_BlackQueen:\r
5596       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case EP_BlackFerz:\r
5601       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case EP_BlackWazir:\r
5606       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     case EP_BlackAlfil:\r
5611       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5612       fromX = fromY = -1;\r
5613       break;\r
5614 \r
5615     case EP_BlackCannon:\r
5616       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5617       fromX = fromY = -1;\r
5618       break;\r
5619 \r
5620     case EP_BlackCardinal:\r
5621       EditPositionMenuEvent(BlackCardinal, fromX, fromY);\r
5622       fromX = fromY = -1;\r
5623       break;\r
5624 \r
5625     case EP_BlackMarshall:\r
5626       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5627       fromX = fromY = -1;\r
5628       break;\r
5629 \r
5630     case EP_BlackKing:\r
5631       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5632       fromX = fromY = -1;\r
5633       break;\r
5634 \r
5635     case EP_EmptySquare:\r
5636       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5637       fromX = fromY = -1;\r
5638       break;\r
5639 \r
5640     case EP_ClearBoard:\r
5641       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5642       fromX = fromY = -1;\r
5643       break;\r
5644 \r
5645     case EP_White:\r
5646       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5647       fromX = fromY = -1;\r
5648       break;\r
5649 \r
5650     case EP_Black:\r
5651       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5652       fromX = fromY = -1;\r
5653       break;\r
5654 \r
5655     case EP_Promote:\r
5656       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5657       fromX = fromY = -1;\r
5658       break;\r
5659 \r
5660     case EP_Demote:\r
5661       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5662       fromX = fromY = -1;\r
5663       break;\r
5664 \r
5665     case DP_Pawn:\r
5666       DropMenuEvent(WhitePawn, fromX, fromY);\r
5667       fromX = fromY = -1;\r
5668       break;\r
5669 \r
5670     case DP_Knight:\r
5671       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5672       fromX = fromY = -1;\r
5673       break;\r
5674 \r
5675     case DP_Bishop:\r
5676       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5677       fromX = fromY = -1;\r
5678       break;\r
5679 \r
5680     case DP_Rook:\r
5681       DropMenuEvent(WhiteRook, fromX, fromY);\r
5682       fromX = fromY = -1;\r
5683       break;\r
5684 \r
5685     case DP_Queen:\r
5686       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5687       fromX = fromY = -1;\r
5688       break;\r
5689 \r
5690     default:\r
5691       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5692     }\r
5693     break;\r
5694 \r
5695   case WM_TIMER:\r
5696     switch (wParam) {\r
5697     case CLOCK_TIMER_ID:\r
5698       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5699       clockTimerEvent = 0;\r
5700       DecrementClocks(); /* call into back end */\r
5701       break;\r
5702     case LOAD_GAME_TIMER_ID:\r
5703       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5704       loadGameTimerEvent = 0;\r
5705       AutoPlayGameLoop(); /* call into back end */\r
5706       break;\r
5707     case ANALYSIS_TIMER_ID:\r
5708       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5709           appData.periodicUpdates) {\r
5710         AnalysisPeriodicEvent(0);\r
5711       } else {\r
5712         KillTimer(hwnd, analysisTimerEvent);\r
5713         analysisTimerEvent = 0;\r
5714       }\r
5715       break;\r
5716     case DELAYED_TIMER_ID:\r
5717       KillTimer(hwnd, delayedTimerEvent);\r
5718       delayedTimerEvent = 0;\r
5719       delayedTimerCallback();\r
5720       break;\r
5721     }\r
5722     break;\r
5723 \r
5724   case WM_USER_Input:\r
5725     InputEvent(hwnd, message, wParam, lParam);\r
5726     break;\r
5727 \r
5728   /* [AS] Also move "attached" child windows */\r
5729   case WM_WINDOWPOSCHANGING:\r
5730     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5731         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5732 \r
5733         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5734             /* Window is moving */\r
5735             RECT rcMain;\r
5736 \r
5737             GetWindowRect( hwnd, &rcMain );\r
5738             \r
5739             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5740             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5741             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5742         }\r
5743     }\r
5744     break;\r
5745 \r
5746   /* [AS] Snapping */\r
5747   case WM_ENTERSIZEMOVE:\r
5748     if (hwnd == hwndMain) {\r
5749       doingSizing = TRUE;\r
5750       lastSizing = 0;\r
5751     }\r
5752     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5753     break;\r
5754 \r
5755   case WM_SIZING:\r
5756     if (hwnd == hwndMain) {\r
5757       lastSizing = wParam;\r
5758     }\r
5759     break;\r
5760 \r
5761   case WM_MOVING:\r
5762       return OnMoving( &sd, hwnd, wParam, lParam );\r
5763 \r
5764   case WM_EXITSIZEMOVE:\r
5765     if (hwnd == hwndMain) {\r
5766       RECT client;\r
5767       doingSizing = FALSE;\r
5768       InvalidateRect(hwnd, &boardRect, FALSE);\r
5769       GetClientRect(hwnd, &client);\r
5770       ResizeBoard(client.right, client.bottom, lastSizing);\r
5771       lastSizing = 0;\r
5772     }\r
5773     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5774     break;\r
5775 \r
5776   case WM_DESTROY: /* message: window being destroyed */\r
5777     PostQuitMessage(0);\r
5778     break;\r
5779 \r
5780   case WM_CLOSE:\r
5781     if (hwnd == hwndMain) {\r
5782       ExitEvent(0);\r
5783     }\r
5784     break;\r
5785 \r
5786   default:      /* Passes it on if unprocessed */\r
5787     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5788   }\r
5789   return 0;\r
5790 }\r
5791 \r
5792 /*---------------------------------------------------------------------------*\\r
5793  *\r
5794  * Misc utility routines\r
5795  *\r
5796 \*---------------------------------------------------------------------------*/\r
5797 \r
5798 /*\r
5799  * Decent random number generator, at least not as bad as Windows\r
5800  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5801  */\r
5802 unsigned int randstate;\r
5803 \r
5804 int\r
5805 myrandom(void)\r
5806 {\r
5807   randstate = randstate * 1664525 + 1013904223;\r
5808   return (int) randstate & 0x7fffffff;\r
5809 }\r
5810 \r
5811 void\r
5812 mysrandom(unsigned int seed)\r
5813 {\r
5814   randstate = seed;\r
5815 }\r
5816 \r
5817 \r
5818 /* \r
5819  * returns TRUE if user selects a different color, FALSE otherwise \r
5820  */\r
5821 \r
5822 BOOL\r
5823 ChangeColor(HWND hwnd, COLORREF *which)\r
5824 {\r
5825   static BOOL firstTime = TRUE;\r
5826   static DWORD customColors[16];\r
5827   CHOOSECOLOR cc;\r
5828   COLORREF newcolor;\r
5829   int i;\r
5830   ColorClass ccl;\r
5831 \r
5832   if (firstTime) {\r
5833     /* Make initial colors in use available as custom colors */\r
5834     /* Should we put the compiled-in defaults here instead? */\r
5835     i = 0;\r
5836     customColors[i++] = lightSquareColor & 0xffffff;\r
5837     customColors[i++] = darkSquareColor & 0xffffff;\r
5838     customColors[i++] = whitePieceColor & 0xffffff;\r
5839     customColors[i++] = blackPieceColor & 0xffffff;\r
5840     customColors[i++] = highlightSquareColor & 0xffffff;\r
5841     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5842 \r
5843     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5844       customColors[i++] = textAttribs[ccl].color;\r
5845     }\r
5846     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5847     firstTime = FALSE;\r
5848   }\r
5849 \r
5850   cc.lStructSize = sizeof(cc);\r
5851   cc.hwndOwner = hwnd;\r
5852   cc.hInstance = NULL;\r
5853   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5854   cc.lpCustColors = (LPDWORD) customColors;\r
5855   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5856 \r
5857   if (!ChooseColor(&cc)) return FALSE;\r
5858 \r
5859   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5860   if (newcolor == *which) return FALSE;\r
5861   *which = newcolor;\r
5862   return TRUE;\r
5863 \r
5864   /*\r
5865   InitDrawingColors();\r
5866   InvalidateRect(hwnd, &boardRect, FALSE);\r
5867   */\r
5868 }\r
5869 \r
5870 BOOLEAN\r
5871 MyLoadSound(MySound *ms)\r
5872 {\r
5873   BOOL ok = FALSE;\r
5874   struct stat st;\r
5875   FILE *f;\r
5876 \r
5877   if (ms->data) free(ms->data);\r
5878   ms->data = NULL;\r
5879 \r
5880   switch (ms->name[0]) {\r
5881   case NULLCHAR:\r
5882     /* Silence */\r
5883     ok = TRUE;\r
5884     break;\r
5885   case '$':\r
5886     /* System sound from Control Panel.  Don't preload here. */\r
5887     ok = TRUE;\r
5888     break;\r
5889   case '!':\r
5890     if (ms->name[1] == NULLCHAR) {\r
5891       /* "!" alone = silence */\r
5892       ok = TRUE;\r
5893     } else {\r
5894       /* Builtin wave resource.  Error if not found. */\r
5895       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5896       if (h == NULL) break;\r
5897       ms->data = (void *)LoadResource(hInst, h);\r
5898       if (h == NULL) break;\r
5899       ok = TRUE;\r
5900     }\r
5901     break;\r
5902   default:\r
5903     /* .wav file.  Error if not found. */\r
5904     f = fopen(ms->name, "rb");\r
5905     if (f == NULL) break;\r
5906     if (fstat(fileno(f), &st) < 0) break;\r
5907     ms->data = malloc(st.st_size);\r
5908     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5909     fclose(f);\r
5910     ok = TRUE;\r
5911     break;\r
5912   }\r
5913   if (!ok) {\r
5914     char buf[MSG_SIZ];\r
5915     sprintf(buf, "Error loading sound %s", ms->name);\r
5916     DisplayError(buf, GetLastError());\r
5917   }\r
5918   return ok;\r
5919 }\r
5920 \r
5921 BOOLEAN\r
5922 MyPlaySound(MySound *ms)\r
5923 {\r
5924   BOOLEAN ok = FALSE;\r
5925   switch (ms->name[0]) {\r
5926   case NULLCHAR:\r
5927     /* Silence */\r
5928     ok = TRUE;\r
5929     break;\r
5930   case '$':\r
5931     /* System sound from Control Panel (deprecated feature).\r
5932        "$" alone or an unset sound name gets default beep (still in use). */\r
5933     if (ms->name[1]) {\r
5934       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5935     }\r
5936     if (!ok) ok = MessageBeep(MB_OK);\r
5937     break; \r
5938   case '!':\r
5939     /* Builtin wave resource, or "!" alone for silence */\r
5940     if (ms->name[1]) {\r
5941       if (ms->data == NULL) return FALSE;\r
5942       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5943     } else {\r
5944       ok = TRUE;\r
5945     }\r
5946     break;\r
5947   default:\r
5948     /* .wav file.  Error if not found. */\r
5949     if (ms->data == NULL) return FALSE;\r
5950     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5951     break;\r
5952   }\r
5953   /* Don't print an error: this can happen innocently if the sound driver\r
5954      is busy; for instance, if another instance of WinBoard is playing\r
5955      a sound at about the same time. */\r
5956 #if 0\r
5957   if (!ok) {\r
5958     char buf[MSG_SIZ];\r
5959     sprintf(buf, "Error playing sound %s", ms->name);\r
5960     DisplayError(buf, GetLastError());\r
5961   }\r
5962 #endif\r
5963   return ok;\r
5964 }\r
5965 \r
5966 \r
5967 LRESULT CALLBACK\r
5968 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5969 {\r
5970   BOOL ok;\r
5971   OPENFILENAME *ofn;\r
5972   static UINT *number; /* gross that this is static */\r
5973 \r
5974   switch (message) {\r
5975   case WM_INITDIALOG: /* message: initialize dialog box */\r
5976     /* Center the dialog over the application window */\r
5977     ofn = (OPENFILENAME *) lParam;\r
5978     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5979       number = (UINT *) ofn->lCustData;\r
5980       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5981     } else {\r
5982       number = NULL;\r
5983     }\r
5984     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5985     return FALSE;  /* Allow for further processing */\r
5986 \r
5987   case WM_COMMAND:\r
5988     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5989       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5990     }\r
5991     return FALSE;  /* Allow for further processing */\r
5992   }\r
5993   return FALSE;\r
5994 }\r
5995 \r
5996 UINT APIENTRY\r
5997 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5998 {\r
5999   static UINT *number;\r
6000   OPENFILENAME *ofname;\r
6001   OFNOTIFY *ofnot;\r
6002   switch (uiMsg) {\r
6003   case WM_INITDIALOG:\r
6004     ofname = (OPENFILENAME *)lParam;\r
6005     number = (UINT *)(ofname->lCustData);\r
6006     break;\r
6007   case WM_NOTIFY:\r
6008     ofnot = (OFNOTIFY *)lParam;\r
6009     if (ofnot->hdr.code == CDN_FILEOK) {\r
6010       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6011     }\r
6012     break;\r
6013   }\r
6014   return 0;\r
6015 }\r
6016 \r
6017 \r
6018 FILE *\r
6019 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
6020                char *nameFilt, char *dlgTitle, UINT *number,\r
6021                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6022 {\r
6023   OPENFILENAME openFileName;\r
6024   char buf1[MSG_SIZ];\r
6025   FILE *f;\r
6026 \r
6027   if (fileName == NULL) fileName = buf1;\r
6028   if (defName == NULL) {\r
6029     strcpy(fileName, "*.");\r
6030     strcat(fileName, defExt);\r
6031   } else {\r
6032     strcpy(fileName, defName);\r
6033   }\r
6034   if (fileTitle) strcpy(fileTitle, "");\r
6035   if (number) *number = 0;\r
6036 \r
6037   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6038   openFileName.hwndOwner         = hwnd;\r
6039   openFileName.hInstance         = (HANDLE) hInst;\r
6040   openFileName.lpstrFilter       = nameFilt;\r
6041   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6042   openFileName.nMaxCustFilter    = 0L;\r
6043   openFileName.nFilterIndex      = 1L;\r
6044   openFileName.lpstrFile         = fileName;\r
6045   openFileName.nMaxFile          = MSG_SIZ;\r
6046   openFileName.lpstrFileTitle    = fileTitle;\r
6047   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6048   openFileName.lpstrInitialDir   = NULL;\r
6049   openFileName.lpstrTitle        = dlgTitle;\r
6050   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6051     | (write ? 0 : OFN_FILEMUSTEXIST) \r
6052     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6053     | (oldDialog ? 0 : OFN_EXPLORER);\r
6054   openFileName.nFileOffset       = 0;\r
6055   openFileName.nFileExtension    = 0;\r
6056   openFileName.lpstrDefExt       = defExt;\r
6057   openFileName.lCustData         = (LONG) number;\r
6058   openFileName.lpfnHook          = oldDialog ?\r
6059     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6060   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6061 \r
6062   if (write ? GetSaveFileName(&openFileName) : \r
6063               GetOpenFileName(&openFileName)) {\r
6064     /* open the file */\r
6065     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
6066     if (f == NULL) {\r
6067       MessageBox(hwnd, "File open failed", NULL,\r
6068                  MB_OK|MB_ICONEXCLAMATION);\r
6069       return NULL;\r
6070     }\r
6071   } else {\r
6072     int err = CommDlgExtendedError();\r
6073     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6074     return FALSE;\r
6075   }\r
6076   return f;\r
6077 }\r
6078 \r
6079 \r
6080 \r
6081 VOID APIENTRY\r
6082 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6083 {\r
6084   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6085 \r
6086   /*\r
6087    * Get the first pop-up menu in the menu template. This is the\r
6088    * menu that TrackPopupMenu displays.\r
6089    */\r
6090   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6091 \r
6092   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6093 \r
6094   /*\r
6095    * TrackPopup uses screen coordinates, so convert the\r
6096    * coordinates of the mouse click to screen coordinates.\r
6097    */\r
6098   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6099 \r
6100   /* Draw and track the floating pop-up menu. */\r
6101   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6102                  pt.x, pt.y, 0, hwnd, NULL);\r
6103 \r
6104   /* Destroy the menu.*/\r
6105   DestroyMenu(hmenu);\r
6106 }\r
6107    \r
6108 typedef struct {\r
6109   HWND hDlg, hText;\r
6110   int sizeX, sizeY, newSizeX, newSizeY;\r
6111   HDWP hdwp;\r
6112 } ResizeEditPlusButtonsClosure;\r
6113 \r
6114 BOOL CALLBACK\r
6115 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6116 {\r
6117   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6118   RECT rect;\r
6119   POINT pt;\r
6120 \r
6121   if (hChild == cl->hText) return TRUE;\r
6122   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6123   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6124   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6125   ScreenToClient(cl->hDlg, &pt);\r
6126   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6127     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6128   return TRUE;\r
6129 }\r
6130 \r
6131 /* Resize a dialog that has a (rich) edit field filling most of\r
6132    the top, with a row of buttons below */\r
6133 VOID\r
6134 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6135 {\r
6136   RECT rectText;\r
6137   int newTextHeight, newTextWidth;\r
6138   ResizeEditPlusButtonsClosure cl;\r
6139   \r
6140   /*if (IsIconic(hDlg)) return;*/\r
6141   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6142   \r
6143   cl.hdwp = BeginDeferWindowPos(8);\r
6144 \r
6145   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6146   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6147   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6148   if (newTextHeight < 0) {\r
6149     newSizeY += -newTextHeight;\r
6150     newTextHeight = 0;\r
6151   }\r
6152   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6153     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6154 \r
6155   cl.hDlg = hDlg;\r
6156   cl.hText = hText;\r
6157   cl.sizeX = sizeX;\r
6158   cl.sizeY = sizeY;\r
6159   cl.newSizeX = newSizeX;\r
6160   cl.newSizeY = newSizeY;\r
6161   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6162 \r
6163   EndDeferWindowPos(cl.hdwp);\r
6164 }\r
6165 \r
6166 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6167 {\r
6168     RECT    rChild, rParent;\r
6169     int     wChild, hChild, wParent, hParent;\r
6170     int     wScreen, hScreen, xNew, yNew;\r
6171     HDC     hdc;\r
6172 \r
6173     /* Get the Height and Width of the child window */\r
6174     GetWindowRect (hwndChild, &rChild);\r
6175     wChild = rChild.right - rChild.left;\r
6176     hChild = rChild.bottom - rChild.top;\r
6177 \r
6178     /* Get the Height and Width of the parent window */\r
6179     GetWindowRect (hwndParent, &rParent);\r
6180     wParent = rParent.right - rParent.left;\r
6181     hParent = rParent.bottom - rParent.top;\r
6182 \r
6183     /* Get the display limits */\r
6184     hdc = GetDC (hwndChild);\r
6185     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6186     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6187     ReleaseDC(hwndChild, hdc);\r
6188 \r
6189     /* Calculate new X position, then adjust for screen */\r
6190     xNew = rParent.left + ((wParent - wChild) /2);\r
6191     if (xNew < 0) {\r
6192         xNew = 0;\r
6193     } else if ((xNew+wChild) > wScreen) {\r
6194         xNew = wScreen - wChild;\r
6195     }\r
6196 \r
6197     /* Calculate new Y position, then adjust for screen */\r
6198     if( mode == 0 ) {\r
6199         yNew = rParent.top  + ((hParent - hChild) /2);\r
6200     }\r
6201     else {\r
6202         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6203     }\r
6204 \r
6205     if (yNew < 0) {\r
6206         yNew = 0;\r
6207     } else if ((yNew+hChild) > hScreen) {\r
6208         yNew = hScreen - hChild;\r
6209     }\r
6210 \r
6211     /* Set it, and return */\r
6212     return SetWindowPos (hwndChild, NULL,\r
6213                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6214 }\r
6215 \r
6216 /* Center one window over another */\r
6217 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6218 {\r
6219     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6220 }\r
6221 \r
6222 /*---------------------------------------------------------------------------*\\r
6223  *\r
6224  * Startup Dialog functions\r
6225  *\r
6226 \*---------------------------------------------------------------------------*/\r
6227 void\r
6228 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6229 {\r
6230   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6231 \r
6232   while (*cd != NULL) {\r
6233     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6234     cd++;\r
6235   }\r
6236 }\r
6237 \r
6238 void\r
6239 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6240 {\r
6241   char buf1[ARG_MAX];\r
6242   int len;\r
6243 \r
6244   if (str[0] == '@') {\r
6245     FILE* f = fopen(str + 1, "r");\r
6246     if (f == NULL) {\r
6247       DisplayFatalError(str + 1, errno, 2);\r
6248       return;\r
6249     }\r
6250     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6251     fclose(f);\r
6252     buf1[len] = NULLCHAR;\r
6253     str = buf1;\r
6254   }\r
6255 \r
6256   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6257 \r
6258   for (;;) {\r
6259     char buf[MSG_SIZ];\r
6260     char *end = strchr(str, '\n');\r
6261     if (end == NULL) return;\r
6262     memcpy(buf, str, end - str);\r
6263     buf[end - str] = NULLCHAR;\r
6264     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6265     str = end + 1;\r
6266   }\r
6267 }\r
6268 \r
6269 void\r
6270 SetStartupDialogEnables(HWND hDlg)\r
6271 {\r
6272   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6273     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6274     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6275   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6276     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6277   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6278     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6279   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6280     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6281   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6282     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6283     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6284     IsDlgButtonChecked(hDlg, OPT_View));\r
6285 }\r
6286 \r
6287 char *\r
6288 QuoteForFilename(char *filename)\r
6289 {\r
6290   int dquote, space;\r
6291   dquote = strchr(filename, '"') != NULL;\r
6292   space = strchr(filename, ' ') != NULL;\r
6293   if (dquote || space) {\r
6294     if (dquote) {\r
6295       return "'";\r
6296     } else {\r
6297       return "\"";\r
6298     }\r
6299   } else {\r
6300     return "";\r
6301   }\r
6302 }\r
6303 \r
6304 VOID\r
6305 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6306 {\r
6307   char buf[MSG_SIZ];\r
6308   char *q;\r
6309 \r
6310   InitComboStringsFromOption(hwndCombo, nthnames);\r
6311   q = QuoteForFilename(nthcp);\r
6312   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6313   if (*nthdir != NULLCHAR) {\r
6314     q = QuoteForFilename(nthdir);\r
6315     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6316   }\r
6317   if (*nthcp == NULLCHAR) {\r
6318     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6319   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6320     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6321     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6322   }\r
6323 }\r
6324 \r
6325 LRESULT CALLBACK\r
6326 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6327 {\r
6328   char buf[MSG_SIZ];\r
6329   HANDLE hwndCombo;\r
6330   char *p;\r
6331 \r
6332   switch (message) {\r
6333   case WM_INITDIALOG:\r
6334     /* Center the dialog */\r
6335     CenterWindow (hDlg, GetDesktopWindow());\r
6336     /* Initialize the dialog items */\r
6337     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6338                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6339                   firstChessProgramNames);\r
6340     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6341                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6342                   secondChessProgramNames);\r
6343     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6344     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6345     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6346     if (*appData.icsHelper != NULLCHAR) {\r
6347       char *q = QuoteForFilename(appData.icsHelper);\r
6348       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6349     }\r
6350     if (*appData.icsHost == NULLCHAR) {\r
6351       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6352       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6353     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6354       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6355       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6356     }\r
6357 \r
6358     if (appData.icsActive) {\r
6359       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6360     }\r
6361     else if (appData.noChessProgram) {\r
6362       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6363     }\r
6364     else {\r
6365       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6366     }\r
6367 \r
6368     SetStartupDialogEnables(hDlg);\r
6369     return TRUE;\r
6370 \r
6371   case WM_COMMAND:\r
6372     switch (LOWORD(wParam)) {\r
6373     case IDOK:\r
6374       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6375         strcpy(buf, "/fcp=");\r
6376         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6377         p = buf;\r
6378         ParseArgs(StringGet, &p);\r
6379         strcpy(buf, "/scp=");\r
6380         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6381         p = buf;\r
6382         ParseArgs(StringGet, &p);\r
6383         appData.noChessProgram = FALSE;\r
6384         appData.icsActive = FALSE;\r
6385       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6386         strcpy(buf, "/ics /icshost=");\r
6387         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6388         p = buf;\r
6389         ParseArgs(StringGet, &p);\r
6390         if (appData.zippyPlay) {\r
6391           strcpy(buf, "/fcp=");\r
6392           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6393           p = buf;\r
6394           ParseArgs(StringGet, &p);\r
6395         }\r
6396       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6397         appData.noChessProgram = TRUE;\r
6398         appData.icsActive = FALSE;\r
6399       } else {\r
6400         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6401                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6402         return TRUE;\r
6403       }\r
6404       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6405         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6406         p = buf;\r
6407         ParseArgs(StringGet, &p);\r
6408       }\r
6409       EndDialog(hDlg, TRUE);\r
6410       return TRUE;\r
6411 \r
6412     case IDCANCEL:\r
6413       ExitEvent(0);\r
6414       return TRUE;\r
6415 \r
6416     case IDM_HELPCONTENTS:\r
6417       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6418         MessageBox (GetFocus(),\r
6419                     "Unable to activate help",\r
6420                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6421       }\r
6422       break;\r
6423 \r
6424     default:\r
6425       SetStartupDialogEnables(hDlg);\r
6426       break;\r
6427     }\r
6428     break;\r
6429   }\r
6430   return FALSE;\r
6431 }\r
6432 \r
6433 /*---------------------------------------------------------------------------*\\r
6434  *\r
6435  * About box dialog functions\r
6436  *\r
6437 \*---------------------------------------------------------------------------*/\r
6438 \r
6439 /* Process messages for "About" dialog box */\r
6440 LRESULT CALLBACK\r
6441 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6442 {\r
6443   switch (message) {\r
6444   case WM_INITDIALOG: /* message: initialize dialog box */\r
6445     /* Center the dialog over the application window */\r
6446     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6447     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6448     return (TRUE);\r
6449 \r
6450   case WM_COMMAND: /* message: received a command */\r
6451     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6452         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6453       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6454       return (TRUE);\r
6455     }\r
6456     break;\r
6457   }\r
6458   return (FALSE);\r
6459 }\r
6460 \r
6461 /*---------------------------------------------------------------------------*\\r
6462  *\r
6463  * Comment Dialog functions\r
6464  *\r
6465 \*---------------------------------------------------------------------------*/\r
6466 \r
6467 LRESULT CALLBACK\r
6468 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6469 {\r
6470   static HANDLE hwndText = NULL;\r
6471   int len, newSizeX, newSizeY, flags;\r
6472   static int sizeX, sizeY;\r
6473   char *str;\r
6474   RECT rect;\r
6475   MINMAXINFO *mmi;\r
6476 \r
6477   switch (message) {\r
6478   case WM_INITDIALOG: /* message: initialize dialog box */\r
6479     /* Initialize the dialog items */\r
6480     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6481     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6482     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6483     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6484     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6485     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6486     SetWindowText(hDlg, commentTitle);\r
6487     if (editComment) {\r
6488       SetFocus(hwndText);\r
6489     } else {\r
6490       SetFocus(GetDlgItem(hDlg, IDOK));\r
6491     }\r
6492     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6493                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6494                 MAKELPARAM(FALSE, 0));\r
6495     /* Size and position the dialog */\r
6496     if (!commentDialog) {\r
6497       commentDialog = hDlg;\r
6498       flags = SWP_NOZORDER;\r
6499       GetClientRect(hDlg, &rect);\r
6500       sizeX = rect.right;\r
6501       sizeY = rect.bottom;\r
6502       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6503           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6504         WINDOWPLACEMENT wp;\r
6505         EnsureOnScreen(&commentX, &commentY);\r
6506         wp.length = sizeof(WINDOWPLACEMENT);\r
6507         wp.flags = 0;\r
6508         wp.showCmd = SW_SHOW;\r
6509         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6510         wp.rcNormalPosition.left = commentX;\r
6511         wp.rcNormalPosition.right = commentX + commentW;\r
6512         wp.rcNormalPosition.top = commentY;\r
6513         wp.rcNormalPosition.bottom = commentY + commentH;\r
6514         SetWindowPlacement(hDlg, &wp);\r
6515 \r
6516         GetClientRect(hDlg, &rect);\r
6517         newSizeX = rect.right;\r
6518         newSizeY = rect.bottom;\r
6519         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6520                               newSizeX, newSizeY);\r
6521         sizeX = newSizeX;\r
6522         sizeY = newSizeY;\r
6523       }\r
6524     }\r
6525     return FALSE;\r
6526 \r
6527   case WM_COMMAND: /* message: received a command */\r
6528     switch (LOWORD(wParam)) {\r
6529     case IDOK:\r
6530       if (editComment) {\r
6531         char *p, *q;\r
6532         /* Read changed options from the dialog box */\r
6533         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6534         len = GetWindowTextLength(hwndText);\r
6535         str = (char *) malloc(len + 1);\r
6536         GetWindowText(hwndText, str, len + 1);\r
6537         p = q = str;\r
6538         while (*q) {\r
6539           if (*q == '\r')\r
6540             q++;\r
6541           else\r
6542             *p++ = *q++;\r
6543         }\r
6544         *p = NULLCHAR;\r
6545         ReplaceComment(commentIndex, str);\r
6546         free(str);\r
6547       }\r
6548       CommentPopDown();\r
6549       return TRUE;\r
6550 \r
6551     case IDCANCEL:\r
6552     case OPT_CancelComment:\r
6553       CommentPopDown();\r
6554       return TRUE;\r
6555 \r
6556     case OPT_ClearComment:\r
6557       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6558       break;\r
6559 \r
6560     case OPT_EditComment:\r
6561       EditCommentEvent();\r
6562       return TRUE;\r
6563 \r
6564     default:\r
6565       break;\r
6566     }\r
6567     break;\r
6568 \r
6569   case WM_SIZE:\r
6570     newSizeX = LOWORD(lParam);\r
6571     newSizeY = HIWORD(lParam);\r
6572     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6573     sizeX = newSizeX;\r
6574     sizeY = newSizeY;\r
6575     break;\r
6576 \r
6577   case WM_GETMINMAXINFO:\r
6578     /* Prevent resizing window too small */\r
6579     mmi = (MINMAXINFO *) lParam;\r
6580     mmi->ptMinTrackSize.x = 100;\r
6581     mmi->ptMinTrackSize.y = 100;\r
6582     break;\r
6583   }\r
6584   return FALSE;\r
6585 }\r
6586 \r
6587 VOID\r
6588 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6589 {\r
6590   FARPROC lpProc;\r
6591   char *p, *q;\r
6592 \r
6593   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6594 \r
6595   if (str == NULL) str = "";\r
6596   p = (char *) malloc(2 * strlen(str) + 2);\r
6597   q = p;\r
6598   while (*str) {\r
6599     if (*str == '\n') *q++ = '\r';\r
6600     *q++ = *str++;\r
6601   }\r
6602   *q = NULLCHAR;\r
6603   if (commentText != NULL) free(commentText);\r
6604 \r
6605   commentIndex = index;\r
6606   commentTitle = title;\r
6607   commentText = p;\r
6608   editComment = edit;\r
6609 \r
6610   if (commentDialog) {\r
6611     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6612     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6613   } else {\r
6614     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6615     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6616                  hwndMain, (DLGPROC)lpProc);\r
6617     FreeProcInstance(lpProc);\r
6618   }\r
6619   commentDialogUp = TRUE;\r
6620 }\r
6621 \r
6622 \r
6623 /*---------------------------------------------------------------------------*\\r
6624  *\r
6625  * Type-in move dialog functions\r
6626  * \r
6627 \*---------------------------------------------------------------------------*/\r
6628 \r
6629 LRESULT CALLBACK\r
6630 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6631 {\r
6632   char move[MSG_SIZ];\r
6633   HWND hInput;\r
6634   ChessMove moveType;\r
6635   int fromX, fromY, toX, toY;\r
6636   char promoChar;\r
6637 \r
6638   switch (message) {\r
6639   case WM_INITDIALOG:\r
6640     move[0] = (char) lParam;\r
6641     move[1] = NULLCHAR;\r
6642     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6643     hInput = GetDlgItem(hDlg, OPT_Move);\r
6644     SetWindowText(hInput, move);\r
6645     SetFocus(hInput);\r
6646     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6647     return FALSE;\r
6648 \r
6649   case WM_COMMAND:\r
6650     switch (LOWORD(wParam)) {\r
6651     case IDOK:\r
6652       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6653         gameMode != Training) {\r
6654         DisplayMoveError("Displayed move is not current");\r
6655       } else {\r
6656         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6657         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6658           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6659           if (gameMode != Training)\r
6660               forwardMostMove = currentMove;\r
6661           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6662         } else {\r
6663           DisplayMoveError("Could not parse move");\r
6664         }\r
6665       }\r
6666       EndDialog(hDlg, TRUE);\r
6667       return TRUE;\r
6668     case IDCANCEL:\r
6669       EndDialog(hDlg, FALSE);\r
6670       return TRUE;\r
6671     default:\r
6672       break;\r
6673     }\r
6674     break;\r
6675   }\r
6676   return FALSE;\r
6677 }\r
6678 \r
6679 VOID\r
6680 PopUpMoveDialog(char firstchar)\r
6681 {\r
6682     FARPROC lpProc;\r
6683     \r
6684     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6685         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6686         gameMode == AnalyzeMode || gameMode == EditGame || \r
6687         gameMode == EditPosition || gameMode == IcsExamining ||\r
6688         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6689         gameMode == Training) {\r
6690       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6691       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6692         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6693       FreeProcInstance(lpProc);\r
6694     }\r
6695 }\r
6696 \r
6697 /*---------------------------------------------------------------------------*\\r
6698  *\r
6699  *  Error dialogs\r
6700  * \r
6701 \*---------------------------------------------------------------------------*/\r
6702 \r
6703 /* Nonmodal error box */\r
6704 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6705                              WPARAM wParam, LPARAM lParam);\r
6706 \r
6707 VOID\r
6708 ErrorPopUp(char *title, char *content)\r
6709 {\r
6710   FARPROC lpProc;\r
6711   char *p, *q;\r
6712   BOOLEAN modal = hwndMain == NULL;\r
6713 \r
6714   p = content;\r
6715   q = errorMessage;\r
6716   while (*p) {\r
6717     if (*p == '\n') {\r
6718       if (modal) {\r
6719         *q++ = ' ';\r
6720         p++;\r
6721       } else {\r
6722         *q++ = '\r';\r
6723         *q++ = *p++;\r
6724       }\r
6725     } else {\r
6726       *q++ = *p++;\r
6727     }\r
6728   }\r
6729   *q = NULLCHAR;\r
6730   strncpy(errorTitle, title, sizeof(errorTitle));\r
6731   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6732   \r
6733   if (modal) {\r
6734     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6735   } else {\r
6736     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6737     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6738                  hwndMain, (DLGPROC)lpProc);\r
6739     FreeProcInstance(lpProc);\r
6740   }\r
6741 }\r
6742 \r
6743 VOID\r
6744 ErrorPopDown()\r
6745 {\r
6746   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6747   if (errorDialog == NULL) return;\r
6748   DestroyWindow(errorDialog);\r
6749   errorDialog = NULL;\r
6750 }\r
6751 \r
6752 LRESULT CALLBACK\r
6753 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6754 {\r
6755   HANDLE hwndText;\r
6756   RECT rChild;\r
6757 \r
6758   switch (message) {\r
6759   case WM_INITDIALOG:\r
6760     GetWindowRect(hDlg, &rChild);\r
6761 \r
6762     /*\r
6763     SetWindowPos(hDlg, NULL, rChild.left,\r
6764       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6765       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6766     */\r
6767 \r
6768     /* \r
6769         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6770         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6771         and it doesn't work when you resize the dialog.\r
6772         For now, just give it a default position.\r
6773     */\r
6774     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6775 \r
6776     errorDialog = hDlg;\r
6777     SetWindowText(hDlg, errorTitle);\r
6778     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6779     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6780     return FALSE;\r
6781 \r
6782   case WM_COMMAND:\r
6783     switch (LOWORD(wParam)) {\r
6784     case IDOK:\r
6785     case IDCANCEL:\r
6786       if (errorDialog == hDlg) errorDialog = NULL;\r
6787       DestroyWindow(hDlg);\r
6788       return TRUE;\r
6789 \r
6790     default:\r
6791       break;\r
6792     }\r
6793     break;\r
6794   }\r
6795   return FALSE;\r
6796 }\r
6797 \r
6798 #ifdef GOTHIC\r
6799 HWND gothicDialog = NULL;\r
6800 \r
6801 LRESULT CALLBACK\r
6802 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6803 {\r
6804   HANDLE hwndText;\r
6805   RECT rChild;\r
6806   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6807 \r
6808   switch (message) {\r
6809   case WM_INITDIALOG:\r
6810     GetWindowRect(hDlg, &rChild);\r
6811 \r
6812     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6813                                                              SWP_NOZORDER);\r
6814 \r
6815     /* \r
6816         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6817         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6818         and it doesn't work when you resize the dialog.\r
6819         For now, just give it a default position.\r
6820     */\r
6821     gothicDialog = hDlg;\r
6822     SetWindowText(hDlg, errorTitle);\r
6823     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6824     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6825     return FALSE;\r
6826 \r
6827   case WM_COMMAND:\r
6828     switch (LOWORD(wParam)) {\r
6829     case IDOK:\r
6830     case IDCANCEL:\r
6831       if (errorDialog == hDlg) errorDialog = NULL;\r
6832       DestroyWindow(hDlg);\r
6833       return TRUE;\r
6834 \r
6835     default:\r
6836       break;\r
6837     }\r
6838     break;\r
6839   }\r
6840   return FALSE;\r
6841 }\r
6842 \r
6843 VOID\r
6844 GothicPopUp(char *title, char up)\r
6845 {\r
6846   FARPROC lpProc;\r
6847   char *p, *q;\r
6848   BOOLEAN modal = hwndMain == NULL;\r
6849 \r
6850   strncpy(errorTitle, title, sizeof(errorTitle));\r
6851   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6852 \r
6853   if(up && gothicDialog == NULL) {\r
6854     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6855     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6856                  hwndMain, (DLGPROC)lpProc);\r
6857     FreeProcInstance(lpProc);\r
6858   } else if(!up && gothicDialog != NULL) {\r
6859     DestroyWindow(gothicDialog);\r
6860     gothicDialog = NULL;\r
6861   }\r
6862 }\r
6863 #endif\r
6864 \r
6865 /*---------------------------------------------------------------------------*\\r
6866  *\r
6867  *  Ics Interaction console functions\r
6868  *\r
6869 \*---------------------------------------------------------------------------*/\r
6870 \r
6871 #define HISTORY_SIZE 64\r
6872 static char *history[HISTORY_SIZE];\r
6873 int histIn = 0, histP = 0;\r
6874 \r
6875 VOID\r
6876 SaveInHistory(char *cmd)\r
6877 {\r
6878   if (history[histIn] != NULL) {\r
6879     free(history[histIn]);\r
6880     history[histIn] = NULL;\r
6881   }\r
6882   if (*cmd == NULLCHAR) return;\r
6883   history[histIn] = StrSave(cmd);\r
6884   histIn = (histIn + 1) % HISTORY_SIZE;\r
6885   if (history[histIn] != NULL) {\r
6886     free(history[histIn]);\r
6887     history[histIn] = NULL;\r
6888   }\r
6889   histP = histIn;\r
6890 }\r
6891 \r
6892 char *\r
6893 PrevInHistory(char *cmd)\r
6894 {\r
6895   int newhp;\r
6896   if (histP == histIn) {\r
6897     if (history[histIn] != NULL) free(history[histIn]);\r
6898     history[histIn] = StrSave(cmd);\r
6899   }\r
6900   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6901   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6902   histP = newhp;\r
6903   return history[histP];\r
6904 }\r
6905 \r
6906 char *\r
6907 NextInHistory()\r
6908 {\r
6909   if (histP == histIn) return NULL;\r
6910   histP = (histP + 1) % HISTORY_SIZE;\r
6911   return history[histP];\r
6912 }\r
6913 \r
6914 typedef struct {\r
6915   char *item;\r
6916   char *command;\r
6917   BOOLEAN getname;\r
6918   BOOLEAN immediate;\r
6919 } IcsTextMenuEntry;\r
6920 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6921 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6922 \r
6923 void\r
6924 ParseIcsTextMenu(char *icsTextMenuString)\r
6925 {\r
6926   int flags = 0;\r
6927   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6928   char *p = icsTextMenuString;\r
6929   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6930     free(e->item);\r
6931     e->item = NULL;\r
6932     if (e->command != NULL) {\r
6933       free(e->command);\r
6934       e->command = NULL;\r
6935     }\r
6936     e++;\r
6937   }\r
6938   e = icsTextMenuEntry;\r
6939   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6940     if (*p == ';' || *p == '\n') {\r
6941       e->item = strdup("-");\r
6942       e->command = NULL;\r
6943       p++;\r
6944     } else if (*p == '-') {\r
6945       e->item = strdup("-");\r
6946       e->command = NULL;\r
6947       p++;\r
6948       if (*p) p++;\r
6949     } else {\r
6950       char *q, *r, *s, *t;\r
6951       char c;\r
6952       q = strchr(p, ',');\r
6953       if (q == NULL) break;\r
6954       *q = NULLCHAR;\r
6955       r = strchr(q + 1, ',');\r
6956       if (r == NULL) break;\r
6957       *r = NULLCHAR;\r
6958       s = strchr(r + 1, ',');\r
6959       if (s == NULL) break;\r
6960       *s = NULLCHAR;\r
6961       c = ';';\r
6962       t = strchr(s + 1, c);\r
6963       if (t == NULL) {\r
6964         c = '\n';\r
6965         t = strchr(s + 1, c);\r
6966       }\r
6967       if (t != NULL) *t = NULLCHAR;\r
6968       e->item = strdup(p);\r
6969       e->command = strdup(q + 1);\r
6970       e->getname = *(r + 1) != '0';\r
6971       e->immediate = *(s + 1) != '0';\r
6972       *q = ',';\r
6973       *r = ',';\r
6974       *s = ',';\r
6975       if (t == NULL) break;\r
6976       *t = c;\r
6977       p = t + 1;\r
6978     }\r
6979     e++;\r
6980   } \r
6981 }\r
6982 \r
6983 HMENU\r
6984 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6985 {\r
6986   HMENU hmenu, h;\r
6987   int i = 0;\r
6988   hmenu = LoadMenu(hInst, "TextMenu");\r
6989   h = GetSubMenu(hmenu, 0);\r
6990   while (e->item) {\r
6991     if (strcmp(e->item, "-") == 0) {\r
6992       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6993     } else {\r
6994       if (e->item[0] == '|') {\r
6995         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6996                    IDM_CommandX + i, &e->item[1]);\r
6997       } else {\r
6998         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6999       }\r
7000     }\r
7001     e++;\r
7002     i++;\r
7003   } \r
7004   return hmenu;\r
7005 }\r
7006 \r
7007 WNDPROC consoleTextWindowProc;\r
7008 \r
7009 void\r
7010 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7011 {\r
7012   char buf[MSG_SIZ], name[MSG_SIZ];\r
7013   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7014   CHARRANGE sel;\r
7015 \r
7016   if (!getname) {\r
7017     SetWindowText(hInput, command);\r
7018     if (immediate) {\r
7019       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7020     } else {\r
7021       sel.cpMin = 999999;\r
7022       sel.cpMax = 999999;\r
7023       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7024       SetFocus(hInput);\r
7025     }\r
7026     return;\r
7027   }    \r
7028   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7029   if (sel.cpMin == sel.cpMax) {\r
7030     /* Expand to surrounding word */\r
7031     TEXTRANGE tr;\r
7032     do {\r
7033       tr.chrg.cpMax = sel.cpMin;\r
7034       tr.chrg.cpMin = --sel.cpMin;\r
7035       if (sel.cpMin < 0) break;\r
7036       tr.lpstrText = name;\r
7037       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7038     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7039     sel.cpMin++;\r
7040 \r
7041     do {\r
7042       tr.chrg.cpMin = sel.cpMax;\r
7043       tr.chrg.cpMax = ++sel.cpMax;\r
7044       tr.lpstrText = name;\r
7045       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7046     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7047     sel.cpMax--;\r
7048 \r
7049     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7050       MessageBeep(MB_ICONEXCLAMATION);\r
7051       return;\r
7052     }\r
7053     tr.chrg = sel;\r
7054     tr.lpstrText = name;\r
7055     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7056   } else {\r
7057     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7058       MessageBeep(MB_ICONEXCLAMATION);\r
7059       return;\r
7060     }\r
7061     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7062   }\r
7063   if (immediate) {\r
7064     sprintf(buf, "%s %s", command, name);\r
7065     SetWindowText(hInput, buf);\r
7066     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7067   } else {\r
7068     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7069     SetWindowText(hInput, buf);\r
7070     sel.cpMin = 999999;\r
7071     sel.cpMax = 999999;\r
7072     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7073     SetFocus(hInput);\r
7074   }\r
7075 }\r
7076 \r
7077 LRESULT CALLBACK \r
7078 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7079 {\r
7080   HWND hInput;\r
7081   CHARRANGE sel;\r
7082 \r
7083   switch (message) {\r
7084   case WM_KEYDOWN:\r
7085     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7086     switch (wParam) {\r
7087     case VK_PRIOR:\r
7088       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7089       return 0;\r
7090     case VK_NEXT:\r
7091       sel.cpMin = 999999;\r
7092       sel.cpMax = 999999;\r
7093       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7094       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7095       return 0;\r
7096     }\r
7097     break;\r
7098   case WM_CHAR:\r
7099     if (wParam == '\t') {\r
7100       if (GetKeyState(VK_SHIFT) < 0) {\r
7101         /* shifted */\r
7102         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7103         if (buttonDesc[0].hwnd) {\r
7104           SetFocus(buttonDesc[0].hwnd);\r
7105         } else {\r
7106           SetFocus(hwndMain);\r
7107         }\r
7108       } else {\r
7109         /* unshifted */\r
7110         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7111       }\r
7112     } else {\r
7113       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7114       SetFocus(hInput);\r
7115       SendMessage(hInput, message, wParam, lParam);\r
7116     }\r
7117     return 0;\r
7118   case WM_PASTE:\r
7119     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7120     SetFocus(hInput);\r
7121     return SendMessage(hInput, message, wParam, lParam);\r
7122   case WM_MBUTTONDOWN:\r
7123     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7124   case WM_RBUTTONDOWN:\r
7125     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7126       /* Move selection here if it was empty */\r
7127       POINT pt;\r
7128       pt.x = LOWORD(lParam);\r
7129       pt.y = HIWORD(lParam);\r
7130       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7131       if (sel.cpMin == sel.cpMax) {\r
7132         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7133         sel.cpMax = sel.cpMin;\r
7134         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7135       }\r
7136       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7137     }\r
7138     return 0;\r
7139   case WM_RBUTTONUP:\r
7140     if (GetKeyState(VK_SHIFT) & ~1) {\r
7141       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7142         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7143     } else {\r
7144       POINT pt;\r
7145       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7146       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7147       if (sel.cpMin == sel.cpMax) {\r
7148         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7149         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7150       }\r
7151       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7152         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7153       }\r
7154       pt.x = LOWORD(lParam);\r
7155       pt.y = HIWORD(lParam);\r
7156       MenuPopup(hwnd, pt, hmenu, -1);\r
7157     }\r
7158     return 0;\r
7159   case WM_COMMAND:\r
7160     switch (LOWORD(wParam)) {\r
7161     case IDM_QuickPaste:\r
7162       {\r
7163         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7164         if (sel.cpMin == sel.cpMax) {\r
7165           MessageBeep(MB_ICONEXCLAMATION);\r
7166           return 0;\r
7167         }\r
7168         SendMessage(hwnd, WM_COPY, 0, 0);\r
7169         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7170         SendMessage(hInput, WM_PASTE, 0, 0);\r
7171         SetFocus(hInput);\r
7172         return 0;\r
7173       }\r
7174     case IDM_Cut:\r
7175       SendMessage(hwnd, WM_CUT, 0, 0);\r
7176       return 0;\r
7177     case IDM_Paste:\r
7178       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7179       return 0;\r
7180     case IDM_Copy:\r
7181       SendMessage(hwnd, WM_COPY, 0, 0);\r
7182       return 0;\r
7183     default:\r
7184       {\r
7185         int i = LOWORD(wParam) - IDM_CommandX;\r
7186         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7187             icsTextMenuEntry[i].command != NULL) {\r
7188           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7189                    icsTextMenuEntry[i].getname,\r
7190                    icsTextMenuEntry[i].immediate);\r
7191           return 0;\r
7192         }\r
7193       }\r
7194       break;\r
7195     }\r
7196     break;\r
7197   }\r
7198   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7199 }\r
7200 \r
7201 WNDPROC consoleInputWindowProc;\r
7202 \r
7203 LRESULT CALLBACK\r
7204 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7205 {\r
7206   char buf[MSG_SIZ];\r
7207   char *p;\r
7208   static BOOL sendNextChar = FALSE;\r
7209   static BOOL quoteNextChar = FALSE;\r
7210   InputSource *is = consoleInputSource;\r
7211   CHARFORMAT cf;\r
7212   CHARRANGE sel;\r
7213 \r
7214   switch (message) {\r
7215   case WM_CHAR:\r
7216     if (!appData.localLineEditing || sendNextChar) {\r
7217       is->buf[0] = (CHAR) wParam;\r
7218       is->count = 1;\r
7219       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7220       sendNextChar = FALSE;\r
7221       return 0;\r
7222     }\r
7223     if (quoteNextChar) {\r
7224       buf[0] = (char) wParam;\r
7225       buf[1] = NULLCHAR;\r
7226       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7227       quoteNextChar = FALSE;\r
7228       return 0;\r
7229     }\r
7230     switch (wParam) {\r
7231     case '\r':   /* Enter key */\r
7232       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7233       if (consoleEcho) SaveInHistory(is->buf);\r
7234       is->buf[is->count++] = '\n';\r
7235       is->buf[is->count] = NULLCHAR;\r
7236       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7237       if (consoleEcho) {\r
7238         ConsoleOutput(is->buf, is->count, TRUE);\r
7239       } else if (appData.localLineEditing) {\r
7240         ConsoleOutput("\n", 1, TRUE);\r
7241       }\r
7242       /* fall thru */\r
7243     case '\033': /* Escape key */\r
7244       SetWindowText(hwnd, "");\r
7245       cf.cbSize = sizeof(CHARFORMAT);\r
7246       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7247       if (consoleEcho) {\r
7248         cf.crTextColor = textAttribs[ColorNormal].color;\r
7249       } else {\r
7250         cf.crTextColor = COLOR_ECHOOFF;\r
7251       }\r
7252       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7253       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7254       return 0;\r
7255     case '\t':   /* Tab key */\r
7256       if (GetKeyState(VK_SHIFT) < 0) {\r
7257         /* shifted */\r
7258         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7259       } else {\r
7260         /* unshifted */\r
7261         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7262         if (buttonDesc[0].hwnd) {\r
7263           SetFocus(buttonDesc[0].hwnd);\r
7264         } else {\r
7265           SetFocus(hwndMain);\r
7266         }\r
7267       }\r
7268       return 0;\r
7269     case '\023': /* Ctrl+S */\r
7270       sendNextChar = TRUE;\r
7271       return 0;\r
7272     case '\021': /* Ctrl+Q */\r
7273       quoteNextChar = TRUE;\r
7274       return 0;\r
7275     default:\r
7276       break;\r
7277     }\r
7278     break;\r
7279   case WM_KEYDOWN:\r
7280     switch (wParam) {\r
7281     case VK_UP:\r
7282       GetWindowText(hwnd, buf, MSG_SIZ);\r
7283       p = PrevInHistory(buf);\r
7284       if (p != NULL) {\r
7285         SetWindowText(hwnd, p);\r
7286         sel.cpMin = 999999;\r
7287         sel.cpMax = 999999;\r
7288         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7289         return 0;\r
7290       }\r
7291       break;\r
7292     case VK_DOWN:\r
7293       p = NextInHistory();\r
7294       if (p != NULL) {\r
7295         SetWindowText(hwnd, p);\r
7296         sel.cpMin = 999999;\r
7297         sel.cpMax = 999999;\r
7298         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7299         return 0;\r
7300       }\r
7301       break;\r
7302     case VK_HOME:\r
7303     case VK_END:\r
7304       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7305       /* fall thru */\r
7306     case VK_PRIOR:\r
7307     case VK_NEXT:\r
7308       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7309       return 0;\r
7310     }\r
7311     break;\r
7312   case WM_MBUTTONDOWN:\r
7313     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7314       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7315     break;\r
7316   case WM_RBUTTONUP:\r
7317     if (GetKeyState(VK_SHIFT) & ~1) {\r
7318       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7319         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7320     } else {\r
7321       POINT pt;\r
7322       HMENU hmenu;\r
7323       hmenu = LoadMenu(hInst, "InputMenu");\r
7324       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7325       if (sel.cpMin == sel.cpMax) {\r
7326         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7327         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7328       }\r
7329       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7330         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7331       }\r
7332       pt.x = LOWORD(lParam);\r
7333       pt.y = HIWORD(lParam);\r
7334       MenuPopup(hwnd, pt, hmenu, -1);\r
7335     }\r
7336     return 0;\r
7337   case WM_COMMAND:\r
7338     switch (LOWORD(wParam)) { \r
7339     case IDM_Undo:\r
7340       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7341       return 0;\r
7342     case IDM_SelectAll:\r
7343       sel.cpMin = 0;\r
7344       sel.cpMax = -1; /*999999?*/\r
7345       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7346       return 0;\r
7347     case IDM_Cut:\r
7348       SendMessage(hwnd, WM_CUT, 0, 0);\r
7349       return 0;\r
7350     case IDM_Paste:\r
7351       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7352       return 0;\r
7353     case IDM_Copy:\r
7354       SendMessage(hwnd, WM_COPY, 0, 0);\r
7355       return 0;\r
7356     }\r
7357     break;\r
7358   }\r
7359   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7360 }\r
7361 \r
7362 #define CO_MAX  100000\r
7363 #define CO_TRIM   1000\r
7364 \r
7365 LRESULT CALLBACK\r
7366 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7367 {\r
7368   static SnapData sd;\r
7369   static HWND hText, hInput, hFocus;\r
7370   InputSource *is = consoleInputSource;\r
7371   RECT rect;\r
7372   static int sizeX, sizeY;\r
7373   int newSizeX, newSizeY;\r
7374   MINMAXINFO *mmi;\r
7375 \r
7376   switch (message) {\r
7377   case WM_INITDIALOG: /* message: initialize dialog box */\r
7378     hwndConsole = hDlg;\r
7379     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7380     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7381     SetFocus(hInput);\r
7382     consoleTextWindowProc = (WNDPROC)\r
7383       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7384     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7385     consoleInputWindowProc = (WNDPROC)\r
7386       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7387     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7388     Colorize(ColorNormal, TRUE);\r
7389     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7390     ChangedConsoleFont();\r
7391     GetClientRect(hDlg, &rect);\r
7392     sizeX = rect.right;\r
7393     sizeY = rect.bottom;\r
7394     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7395         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7396       WINDOWPLACEMENT wp;\r
7397       EnsureOnScreen(&consoleX, &consoleY);\r
7398       wp.length = sizeof(WINDOWPLACEMENT);\r
7399       wp.flags = 0;\r
7400       wp.showCmd = SW_SHOW;\r
7401       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7402       wp.rcNormalPosition.left = consoleX;\r
7403       wp.rcNormalPosition.right = consoleX + consoleW;\r
7404       wp.rcNormalPosition.top = consoleY;\r
7405       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7406       SetWindowPlacement(hDlg, &wp);\r
7407     }\r
7408     return FALSE;\r
7409 \r
7410   case WM_SETFOCUS:\r
7411     SetFocus(hInput);\r
7412     return 0;\r
7413 \r
7414   case WM_CLOSE:\r
7415     ExitEvent(0);\r
7416     /* not reached */\r
7417     break;\r
7418 \r
7419   case WM_SIZE:\r
7420     if (IsIconic(hDlg)) break;\r
7421     newSizeX = LOWORD(lParam);\r
7422     newSizeY = HIWORD(lParam);\r
7423     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7424       RECT rectText, rectInput;\r
7425       POINT pt;\r
7426       int newTextHeight, newTextWidth;\r
7427       GetWindowRect(hText, &rectText);\r
7428       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7429       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7430       if (newTextHeight < 0) {\r
7431         newSizeY += -newTextHeight;\r
7432         newTextHeight = 0;\r
7433       }\r
7434       SetWindowPos(hText, NULL, 0, 0,\r
7435         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7436       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7437       pt.x = rectInput.left;\r
7438       pt.y = rectInput.top + newSizeY - sizeY;\r
7439       ScreenToClient(hDlg, &pt);\r
7440       SetWindowPos(hInput, NULL, \r
7441         pt.x, pt.y, /* needs client coords */   \r
7442         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7443         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7444     }\r
7445     sizeX = newSizeX;\r
7446     sizeY = newSizeY;\r
7447     break;\r
7448 \r
7449   case WM_GETMINMAXINFO:\r
7450     /* Prevent resizing window too small */\r
7451     mmi = (MINMAXINFO *) lParam;\r
7452     mmi->ptMinTrackSize.x = 100;\r
7453     mmi->ptMinTrackSize.y = 100;\r
7454     break;\r
7455 \r
7456   /* [AS] Snapping */\r
7457   case WM_ENTERSIZEMOVE:\r
7458     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7459 \r
7460   case WM_SIZING:\r
7461     return OnSizing( &sd, hDlg, wParam, lParam );\r
7462 \r
7463   case WM_MOVING:\r
7464     return OnMoving( &sd, hDlg, wParam, lParam );\r
7465 \r
7466   case WM_EXITSIZEMOVE:\r
7467     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7468   }\r
7469 \r
7470   return DefWindowProc(hDlg, message, wParam, lParam);\r
7471 }\r
7472 \r
7473 \r
7474 VOID\r
7475 ConsoleCreate()\r
7476 {\r
7477   HWND hCons;\r
7478   if (hwndConsole) return;\r
7479   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7480   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7481 }\r
7482 \r
7483 \r
7484 VOID\r
7485 ConsoleOutput(char* data, int length, int forceVisible)\r
7486 {\r
7487   HWND hText;\r
7488   int trim, exlen;\r
7489   char *p, *q;\r
7490   char buf[CO_MAX+1];\r
7491   POINT pEnd;\r
7492   RECT rect;\r
7493   static int delayLF = 0;\r
7494   CHARRANGE savesel, sel;\r
7495 \r
7496   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7497   p = data;\r
7498   q = buf;\r
7499   if (delayLF) {\r
7500     *q++ = '\r';\r
7501     *q++ = '\n';\r
7502     delayLF = 0;\r
7503   }\r
7504   while (length--) {\r
7505     if (*p == '\n') {\r
7506       if (*++p) {\r
7507         *q++ = '\r';\r
7508         *q++ = '\n';\r
7509       } else {\r
7510         delayLF = 1;\r
7511       }\r
7512     } else if (*p == '\007') {\r
7513        MyPlaySound(&sounds[(int)SoundBell]);\r
7514        p++;\r
7515     } else {\r
7516       *q++ = *p++;\r
7517     }\r
7518   }\r
7519   *q = NULLCHAR;\r
7520   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7521   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7522   /* Save current selection */\r
7523   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7524   exlen = GetWindowTextLength(hText);\r
7525   /* Find out whether current end of text is visible */\r
7526   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7527   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7528   /* Trim existing text if it's too long */\r
7529   if (exlen + (q - buf) > CO_MAX) {\r
7530     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7531     sel.cpMin = 0;\r
7532     sel.cpMax = trim;\r
7533     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7534     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7535     exlen -= trim;\r
7536     savesel.cpMin -= trim;\r
7537     savesel.cpMax -= trim;\r
7538     if (exlen < 0) exlen = 0;\r
7539     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7540     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7541   }\r
7542   /* Append the new text */\r
7543   sel.cpMin = exlen;\r
7544   sel.cpMax = exlen;\r
7545   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7546   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7547   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7548   if (forceVisible || exlen == 0 ||\r
7549       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7550        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7551     /* Scroll to make new end of text visible if old end of text\r
7552        was visible or new text is an echo of user typein */\r
7553     sel.cpMin = 9999999;\r
7554     sel.cpMax = 9999999;\r
7555     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7556     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7557     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7558     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7559   }\r
7560   if (savesel.cpMax == exlen || forceVisible) {\r
7561     /* Move insert point to new end of text if it was at the old\r
7562        end of text or if the new text is an echo of user typein */\r
7563     sel.cpMin = 9999999;\r
7564     sel.cpMax = 9999999;\r
7565     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7566   } else {\r
7567     /* Restore previous selection */\r
7568     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7569   }\r
7570   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7571 }\r
7572 \r
7573 /*---------*/\r
7574 \r
7575 \r
7576 void\r
7577 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7578 {\r
7579   char buf[100];\r
7580   char *str;\r
7581   COLORREF oldFg, oldBg;\r
7582   HFONT oldFont;\r
7583   RECT rect;\r
7584 \r
7585   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7586 \r
7587   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7588   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7589   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7590 \r
7591   rect.left = x;\r
7592   rect.right = x + squareSize;\r
7593   rect.top  = y;\r
7594   rect.bottom = y + squareSize;\r
7595   str = buf;\r
7596 \r
7597   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7598                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7599              y, ETO_CLIPPED|ETO_OPAQUE,\r
7600              &rect, str, strlen(str), NULL);\r
7601 \r
7602   (void) SetTextColor(hdc, oldFg);\r
7603   (void) SetBkColor(hdc, oldBg);\r
7604   (void) SelectObject(hdc, oldFont);\r
7605 }\r
7606 \r
7607 void\r
7608 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7609               RECT *rect, char *color, char *flagFell)\r
7610 {\r
7611   char buf[100];\r
7612   char *str;\r
7613   COLORREF oldFg, oldBg;\r
7614   HFONT oldFont;\r
7615 \r
7616   if (appData.clockMode) {\r
7617     if (tinyLayout)\r
7618       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7619     else\r
7620       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7621     str = buf;\r
7622   } else {\r
7623     str = color;\r
7624   }\r
7625 \r
7626   if (highlight) {\r
7627     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7628     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7629   } else {\r
7630     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7631     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7632   }\r
7633   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7634 \r
7635   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7636              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7637              rect, str, strlen(str), NULL);\r
7638 \r
7639   (void) SetTextColor(hdc, oldFg);\r
7640   (void) SetBkColor(hdc, oldBg);\r
7641   (void) SelectObject(hdc, oldFont);\r
7642 }\r
7643 \r
7644 \r
7645 int\r
7646 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7647            OVERLAPPED *ovl)\r
7648 {\r
7649   int ok, err;\r
7650 \r
7651   /* [AS]  */\r
7652   if( count <= 0 ) {\r
7653     if (appData.debugMode) {\r
7654       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7655     }\r
7656 \r
7657     return ERROR_INVALID_USER_BUFFER;\r
7658   }\r
7659 \r
7660   ResetEvent(ovl->hEvent);\r
7661   ovl->Offset = ovl->OffsetHigh = 0;\r
7662   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7663   if (ok) {\r
7664     err = NO_ERROR;\r
7665   } else {\r
7666     err = GetLastError();\r
7667     if (err == ERROR_IO_PENDING) {\r
7668       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7669       if (ok)\r
7670         err = NO_ERROR;\r
7671       else\r
7672         err = GetLastError();\r
7673     }\r
7674   }\r
7675   return err;\r
7676 }\r
7677 \r
7678 int\r
7679 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7680             OVERLAPPED *ovl)\r
7681 {\r
7682   int ok, err;\r
7683 \r
7684   ResetEvent(ovl->hEvent);\r
7685   ovl->Offset = ovl->OffsetHigh = 0;\r
7686   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7687   if (ok) {\r
7688     err = NO_ERROR;\r
7689   } else {\r
7690     err = GetLastError();\r
7691     if (err == ERROR_IO_PENDING) {\r
7692       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7693       if (ok)\r
7694         err = NO_ERROR;\r
7695       else\r
7696         err = GetLastError();\r
7697     }\r
7698   }\r
7699   return err;\r
7700 }\r
7701 \r
7702 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7703 void CheckForInputBufferFull( InputSource * is )\r
7704 {\r
7705     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7706         /* Look for end of line */\r
7707         char * p = is->buf;\r
7708         \r
7709         while( p < is->next && *p != '\n' ) {\r
7710             p++;\r
7711         }\r
7712 \r
7713         if( p >= is->next ) {\r
7714             if (appData.debugMode) {\r
7715                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7716             }\r
7717 \r
7718             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7719             is->count = (DWORD) -1;\r
7720             is->next = is->buf;\r
7721         }\r
7722     }\r
7723 }\r
7724 \r
7725 DWORD\r
7726 InputThread(LPVOID arg)\r
7727 {\r
7728   InputSource *is;\r
7729   OVERLAPPED ovl;\r
7730 \r
7731   is = (InputSource *) arg;\r
7732   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7733   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7734   while (is->hThread != NULL) {\r
7735     is->error = DoReadFile(is->hFile, is->next,\r
7736                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7737                            &is->count, &ovl);\r
7738     if (is->error == NO_ERROR) {\r
7739       is->next += is->count;\r
7740     } else {\r
7741       if (is->error == ERROR_BROKEN_PIPE) {\r
7742         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7743         is->count = 0;\r
7744       } else {\r
7745         is->count = (DWORD) -1;\r
7746         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7747         break; \r
7748       }\r
7749     }\r
7750 \r
7751     CheckForInputBufferFull( is );\r
7752 \r
7753     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7754 \r
7755     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7756 \r
7757     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7758   }\r
7759 \r
7760   CloseHandle(ovl.hEvent);\r
7761   CloseHandle(is->hFile);\r
7762 \r
7763   if (appData.debugMode) {\r
7764     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7765   }\r
7766 \r
7767   return 0;\r
7768 }\r
7769 \r
7770 \r
7771 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7772 DWORD\r
7773 NonOvlInputThread(LPVOID arg)\r
7774 {\r
7775   InputSource *is;\r
7776   char *p, *q;\r
7777   int i;\r
7778   char prev;\r
7779 \r
7780   is = (InputSource *) arg;\r
7781   while (is->hThread != NULL) {\r
7782     is->error = ReadFile(is->hFile, is->next,\r
7783                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7784                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7785     if (is->error == NO_ERROR) {\r
7786       /* Change CRLF to LF */\r
7787       if (is->next > is->buf) {\r
7788         p = is->next - 1;\r
7789         i = is->count + 1;\r
7790       } else {\r
7791         p = is->next;\r
7792         i = is->count;\r
7793       }\r
7794       q = p;\r
7795       prev = NULLCHAR;\r
7796       while (i > 0) {\r
7797         if (prev == '\r' && *p == '\n') {\r
7798           *(q-1) = '\n';\r
7799           is->count--;\r
7800         } else { \r
7801           *q++ = *p;\r
7802         }\r
7803         prev = *p++;\r
7804         i--;\r
7805       }\r
7806       *q = NULLCHAR;\r
7807       is->next = q;\r
7808     } else {\r
7809       if (is->error == ERROR_BROKEN_PIPE) {\r
7810         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7811         is->count = 0; \r
7812       } else {\r
7813         is->count = (DWORD) -1;\r
7814       }\r
7815     }\r
7816 \r
7817     CheckForInputBufferFull( is );\r
7818 \r
7819     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7820 \r
7821     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7822 \r
7823     if (is->count < 0) break;  /* Quit on error */\r
7824   }\r
7825   CloseHandle(is->hFile);\r
7826   return 0;\r
7827 }\r
7828 \r
7829 DWORD\r
7830 SocketInputThread(LPVOID arg)\r
7831 {\r
7832   InputSource *is;\r
7833 \r
7834   is = (InputSource *) arg;\r
7835   while (is->hThread != NULL) {\r
7836     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7837     if ((int)is->count == SOCKET_ERROR) {\r
7838       is->count = (DWORD) -1;\r
7839       is->error = WSAGetLastError();\r
7840     } else {\r
7841       is->error = NO_ERROR;\r
7842       is->next += is->count;\r
7843       if (is->count == 0 && is->second == is) {\r
7844         /* End of file on stderr; quit with no message */\r
7845         break;\r
7846       }\r
7847     }\r
7848     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7849 \r
7850     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7851 \r
7852     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7853   }\r
7854   return 0;\r
7855 }\r
7856 \r
7857 VOID\r
7858 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7859 {\r
7860   InputSource *is;\r
7861 \r
7862   is = (InputSource *) lParam;\r
7863   if (is->lineByLine) {\r
7864     /* Feed in lines one by one */\r
7865     char *p = is->buf;\r
7866     char *q = p;\r
7867     while (q < is->next) {\r
7868       if (*q++ == '\n') {\r
7869         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7870         p = q;\r
7871       }\r
7872     }\r
7873     \r
7874     /* Move any partial line to the start of the buffer */\r
7875     q = is->buf;\r
7876     while (p < is->next) {\r
7877       *q++ = *p++;\r
7878     }\r
7879     is->next = q;\r
7880 \r
7881     if (is->error != NO_ERROR || is->count == 0) {\r
7882       /* Notify backend of the error.  Note: If there was a partial\r
7883          line at the end, it is not flushed through. */\r
7884       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7885     }\r
7886   } else {\r
7887     /* Feed in the whole chunk of input at once */\r
7888     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7889     is->next = is->buf;\r
7890   }\r
7891 }\r
7892 \r
7893 /*---------------------------------------------------------------------------*\\r
7894  *\r
7895  *  Menu enables. Used when setting various modes.\r
7896  *\r
7897 \*---------------------------------------------------------------------------*/\r
7898 \r
7899 typedef struct {\r
7900   int item;\r
7901   int flags;\r
7902 } Enables;\r
7903 \r
7904 VOID\r
7905 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7906 {\r
7907   while (enab->item > 0) {\r
7908     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7909     enab++;\r
7910   }\r
7911 }\r
7912 \r
7913 Enables gnuEnables[] = {\r
7914   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7924   { -1, -1 }\r
7925 };\r
7926 \r
7927 Enables icsEnables[] = {\r
7928   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7940   { -1, -1 }\r
7941 };\r
7942 \r
7943 #ifdef ZIPPY\r
7944 Enables zippyEnables[] = {\r
7945   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7948   { -1, -1 }\r
7949 };\r
7950 #endif\r
7951 \r
7952 Enables ncpEnables[] = {\r
7953   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7961   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7962   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7968   { -1, -1 }\r
7969 };\r
7970 \r
7971 Enables trainingOnEnables[] = {\r
7972   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7980   { -1, -1 }\r
7981 };\r
7982 \r
7983 Enables trainingOffEnables[] = {\r
7984   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7992   { -1, -1 }\r
7993 };\r
7994 \r
7995 /* These modify either ncpEnables or gnuEnables */\r
7996 Enables cmailEnables[] = {\r
7997   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7999   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8000   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8002   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8004   { -1, -1 }\r
8005 };\r
8006 \r
8007 Enables machineThinkingEnables[] = {\r
8008   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8023   { -1, -1 }\r
8024 };\r
8025 \r
8026 Enables userThinkingEnables[] = {\r
8027   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8029   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8031   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8042   { -1, -1 }\r
8043 };\r
8044 \r
8045 /*---------------------------------------------------------------------------*\\r
8046  *\r
8047  *  Front-end interface functions exported by XBoard.\r
8048  *  Functions appear in same order as prototypes in frontend.h.\r
8049  * \r
8050 \*---------------------------------------------------------------------------*/\r
8051 VOID\r
8052 ModeHighlight()\r
8053 {\r
8054   static UINT prevChecked = 0;\r
8055   static int prevPausing = 0;\r
8056   UINT nowChecked;\r
8057 \r
8058   if (pausing != prevPausing) {\r
8059     prevPausing = pausing;\r
8060     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8061                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8062     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8063   }\r
8064 \r
8065   switch (gameMode) {\r
8066   case BeginningOfGame:\r
8067     if (appData.icsActive)\r
8068       nowChecked = IDM_IcsClient;\r
8069     else if (appData.noChessProgram)\r
8070       nowChecked = IDM_EditGame;\r
8071     else\r
8072       nowChecked = IDM_MachineBlack;\r
8073     break;\r
8074   case MachinePlaysBlack:\r
8075     nowChecked = IDM_MachineBlack;\r
8076     break;\r
8077   case MachinePlaysWhite:\r
8078     nowChecked = IDM_MachineWhite;\r
8079     break;\r
8080   case TwoMachinesPlay:\r
8081     nowChecked = IDM_TwoMachines;\r
8082     break;\r
8083   case AnalyzeMode:\r
8084     nowChecked = IDM_AnalysisMode;\r
8085     break;\r
8086   case AnalyzeFile:\r
8087     nowChecked = IDM_AnalyzeFile;\r
8088     break;\r
8089   case EditGame:\r
8090     nowChecked = IDM_EditGame;\r
8091     break;\r
8092   case PlayFromGameFile:\r
8093     nowChecked = IDM_LoadGame;\r
8094     break;\r
8095   case EditPosition:\r
8096     nowChecked = IDM_EditPosition;\r
8097     break;\r
8098   case Training:\r
8099     nowChecked = IDM_Training;\r
8100     break;\r
8101   case IcsPlayingWhite:\r
8102   case IcsPlayingBlack:\r
8103   case IcsObserving:\r
8104   case IcsIdle:\r
8105     nowChecked = IDM_IcsClient;\r
8106     break;\r
8107   default:\r
8108   case EndOfGame:\r
8109     nowChecked = 0;\r
8110     break;\r
8111   }\r
8112   if (prevChecked != 0)\r
8113     (void) CheckMenuItem(GetMenu(hwndMain),\r
8114                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8115   if (nowChecked != 0)\r
8116     (void) CheckMenuItem(GetMenu(hwndMain),\r
8117                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8118 \r
8119   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8120     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8121                           MF_BYCOMMAND|MF_ENABLED);\r
8122   } else {\r
8123     (void) EnableMenuItem(GetMenu(hwndMain), \r
8124                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8125   }\r
8126 \r
8127   prevChecked = nowChecked;\r
8128 }\r
8129 \r
8130 VOID\r
8131 SetICSMode()\r
8132 {\r
8133   HMENU hmenu = GetMenu(hwndMain);\r
8134   SetMenuEnables(hmenu, icsEnables);\r
8135   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8136     MF_BYPOSITION|MF_ENABLED);\r
8137 #ifdef ZIPPY\r
8138   if (appData.zippyPlay) {\r
8139     SetMenuEnables(hmenu, zippyEnables);\r
8140   }\r
8141 #endif\r
8142 }\r
8143 \r
8144 VOID\r
8145 SetGNUMode()\r
8146 {\r
8147   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8148 }\r
8149 \r
8150 VOID\r
8151 SetNCPMode()\r
8152 {\r
8153   HMENU hmenu = GetMenu(hwndMain);\r
8154   SetMenuEnables(hmenu, ncpEnables);\r
8155   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8156     MF_BYPOSITION|MF_GRAYED);\r
8157     DrawMenuBar(hwndMain);\r
8158 }\r
8159 \r
8160 VOID\r
8161 SetCmailMode()\r
8162 {\r
8163   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8164 }\r
8165 \r
8166 VOID \r
8167 SetTrainingModeOn()\r
8168 {\r
8169   int i;\r
8170   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8171   for (i = 0; i < N_BUTTONS; i++) {\r
8172     if (buttonDesc[i].hwnd != NULL)\r
8173       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8174   }\r
8175   CommentPopDown();\r
8176 }\r
8177 \r
8178 VOID SetTrainingModeOff()\r
8179 {\r
8180   int i;\r
8181   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8182   for (i = 0; i < N_BUTTONS; i++) {\r
8183     if (buttonDesc[i].hwnd != NULL)\r
8184       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8185   }\r
8186 }\r
8187 \r
8188 \r
8189 VOID\r
8190 SetUserThinkingEnables()\r
8191 {\r
8192   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8193 }\r
8194 \r
8195 VOID\r
8196 SetMachineThinkingEnables()\r
8197 {\r
8198   HMENU hMenu = GetMenu(hwndMain);\r
8199   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8200 \r
8201   SetMenuEnables(hMenu, machineThinkingEnables);\r
8202 \r
8203   if (gameMode == MachinePlaysBlack) {\r
8204     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8205   } else if (gameMode == MachinePlaysWhite) {\r
8206     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8207   } else if (gameMode == TwoMachinesPlay) {\r
8208     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8209   }\r
8210 }\r
8211 \r
8212 \r
8213 VOID\r
8214 DisplayTitle(char *str)\r
8215 {\r
8216   char title[MSG_SIZ], *host;\r
8217   if (str[0] != NULLCHAR) {\r
8218     strcpy(title, str);\r
8219   } else if (appData.icsActive) {\r
8220     if (appData.icsCommPort[0] != NULLCHAR)\r
8221       host = "ICS";\r
8222     else \r
8223       host = appData.icsHost;\r
8224     sprintf(title, "%s: %s", szTitle, host);\r
8225   } else if (appData.noChessProgram) {\r
8226     strcpy(title, szTitle);\r
8227   } else {\r
8228     strcpy(title, szTitle);\r
8229     strcat(title, ": ");\r
8230     strcat(title, first.tidy);\r
8231   }\r
8232   SetWindowText(hwndMain, title);\r
8233 }\r
8234 \r
8235 \r
8236 VOID\r
8237 DisplayMessage(char *str1, char *str2)\r
8238 {\r
8239   HDC hdc;\r
8240   HFONT oldFont;\r
8241   int remain = MESSAGE_TEXT_MAX - 1;\r
8242   int len;\r
8243 \r
8244   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8245   messageText[0] = NULLCHAR;\r
8246   if (*str1) {\r
8247     len = strlen(str1);\r
8248     if (len > remain) len = remain;\r
8249     strncpy(messageText, str1, len);\r
8250     messageText[len] = NULLCHAR;\r
8251     remain -= len;\r
8252   }\r
8253   if (*str2 && remain >= 2) {\r
8254     if (*str1) {\r
8255       strcat(messageText, "  ");\r
8256       remain -= 2;\r
8257     }\r
8258     len = strlen(str2);\r
8259     if (len > remain) len = remain;\r
8260     strncat(messageText, str2, len);\r
8261   }\r
8262   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8263 \r
8264   if (IsIconic(hwndMain)) return;\r
8265   hdc = GetDC(hwndMain);\r
8266   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8267   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8268              &messageRect, messageText, strlen(messageText), NULL);\r
8269   (void) SelectObject(hdc, oldFont);\r
8270   (void) ReleaseDC(hwndMain, hdc);\r
8271 }\r
8272 \r
8273 VOID\r
8274 DisplayError(char *str, int error)\r
8275 {\r
8276   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8277   int len;\r
8278 \r
8279   if (error == 0) {\r
8280     strcpy(buf, str);\r
8281   } else {\r
8282     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8283                         NULL, error, LANG_NEUTRAL,\r
8284                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8285     if (len > 0) {\r
8286       sprintf(buf, "%s:\n%s", str, buf2);\r
8287     } else {\r
8288       ErrorMap *em = errmap;\r
8289       while (em->err != 0 && em->err != error) em++;\r
8290       if (em->err != 0) {\r
8291         sprintf(buf, "%s:\n%s", str, em->msg);\r
8292       } else {\r
8293         sprintf(buf, "%s:\nError code %d", str, error);\r
8294       }\r
8295     }\r
8296   }\r
8297   \r
8298   ErrorPopUp("Error", buf);\r
8299 }\r
8300 \r
8301 \r
8302 VOID\r
8303 DisplayMoveError(char *str)\r
8304 {\r
8305   fromX = fromY = -1;\r
8306   ClearHighlights();\r
8307   DrawPosition(FALSE, NULL);\r
8308   if (appData.popupMoveErrors) {\r
8309     ErrorPopUp("Error", str);\r
8310   } else {\r
8311     DisplayMessage(str, "");\r
8312     moveErrorMessageUp = TRUE;\r
8313   }\r
8314 }\r
8315 \r
8316 VOID\r
8317 DisplayFatalError(char *str, int error, int exitStatus)\r
8318 {\r
8319   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8320   int len;\r
8321   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8322 \r
8323   if (error != 0) {\r
8324     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8325                         NULL, error, LANG_NEUTRAL,\r
8326                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8327     if (len > 0) {\r
8328       sprintf(buf, "%s:\n%s", str, buf2);\r
8329     } else {\r
8330       ErrorMap *em = errmap;\r
8331       while (em->err != 0 && em->err != error) em++;\r
8332       if (em->err != 0) {\r
8333         sprintf(buf, "%s:\n%s", str, em->msg);\r
8334       } else {\r
8335         sprintf(buf, "%s:\nError code %d", str, error);\r
8336       }\r
8337     }\r
8338     str = buf;\r
8339   }\r
8340   if (appData.debugMode) {\r
8341     fprintf(debugFP, "%s: %s\n", label, str);\r
8342   }\r
8343   if (appData.popupExitMessage) {\r
8344     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8345                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8346   }\r
8347   ExitEvent(exitStatus);\r
8348 }\r
8349 \r
8350 \r
8351 VOID\r
8352 DisplayInformation(char *str)\r
8353 {\r
8354   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8355 }\r
8356 \r
8357 \r
8358 VOID\r
8359 DisplayNote(char *str)\r
8360 {\r
8361   ErrorPopUp("Note", str);\r
8362 }\r
8363 \r
8364 \r
8365 typedef struct {\r
8366   char *title, *question, *replyPrefix;\r
8367   ProcRef pr;\r
8368 } QuestionParams;\r
8369 \r
8370 LRESULT CALLBACK\r
8371 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8372 {\r
8373   static QuestionParams *qp;\r
8374   char reply[MSG_SIZ];\r
8375   int len, err;\r
8376 \r
8377   switch (message) {\r
8378   case WM_INITDIALOG:\r
8379     qp = (QuestionParams *) lParam;\r
8380     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8381     SetWindowText(hDlg, qp->title);\r
8382     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8383     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8384     return FALSE;\r
8385 \r
8386   case WM_COMMAND:\r
8387     switch (LOWORD(wParam)) {\r
8388     case IDOK:\r
8389       strcpy(reply, qp->replyPrefix);\r
8390       if (*reply) strcat(reply, " ");\r
8391       len = strlen(reply);\r
8392       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8393       strcat(reply, "\n");\r
8394       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8395       EndDialog(hDlg, TRUE);\r
8396       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8397       return TRUE;\r
8398     case IDCANCEL:\r
8399       EndDialog(hDlg, FALSE);\r
8400       return TRUE;\r
8401     default:\r
8402       break;\r
8403     }\r
8404     break;\r
8405   }\r
8406   return FALSE;\r
8407 }\r
8408 \r
8409 VOID\r
8410 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8411 {\r
8412     QuestionParams qp;\r
8413     FARPROC lpProc;\r
8414     \r
8415     qp.title = title;\r
8416     qp.question = question;\r
8417     qp.replyPrefix = replyPrefix;\r
8418     qp.pr = pr;\r
8419     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8420     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8421       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8422     FreeProcInstance(lpProc);\r
8423 }\r
8424 \r
8425 /* [AS] Pick FRC position */\r
8426 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8427 {\r
8428     static int * lpIndexFRC;\r
8429     BOOL index_is_ok;\r
8430     char buf[16];\r
8431 \r
8432     switch( message )\r
8433     {\r
8434     case WM_INITDIALOG:\r
8435         lpIndexFRC = (int *) lParam;\r
8436 \r
8437         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8438 \r
8439         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8440         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8441         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8442         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8443 \r
8444         break;\r
8445 \r
8446     case WM_COMMAND:\r
8447         switch( LOWORD(wParam) ) {\r
8448         case IDOK:\r
8449             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8450             EndDialog( hDlg, 0 );\r
8451             return TRUE;\r
8452         case IDCANCEL:\r
8453             EndDialog( hDlg, 1 );   \r
8454             return TRUE;\r
8455         case IDC_NFG_Edit:\r
8456             if( HIWORD(wParam) == EN_CHANGE ) {\r
8457                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8458 \r
8459                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8460             }\r
8461             return TRUE;\r
8462         case IDC_NFG_Random:\r
8463             sprintf( buf, "%d", myrandom() % 960 );\r
8464             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8465             return TRUE;\r
8466         }\r
8467 \r
8468         break;\r
8469     }\r
8470 \r
8471     return FALSE;\r
8472 }\r
8473 \r
8474 int NewGameFRC()\r
8475 {\r
8476     int result;\r
8477     int index = appData.defaultFrcPosition;\r
8478     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8479 \r
8480     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8481 \r
8482     if( result == 0 ) {\r
8483         appData.defaultFrcPosition = index;\r
8484     }\r
8485 \r
8486     return result;\r
8487 }\r
8488 \r
8489 /* [AS] Game list options */\r
8490 typedef struct {\r
8491     char id;\r
8492     char * name;\r
8493 } GLT_Item;\r
8494 \r
8495 static GLT_Item GLT_ItemInfo[] = {\r
8496     { GLT_EVENT,      "Event" },\r
8497     { GLT_SITE,       "Site" },\r
8498     { GLT_DATE,       "Date" },\r
8499     { GLT_ROUND,      "Round" },\r
8500     { GLT_PLAYERS,    "Players" },\r
8501     { GLT_RESULT,     "Result" },\r
8502     { GLT_WHITE_ELO,  "White Rating" },\r
8503     { GLT_BLACK_ELO,  "Black Rating" },\r
8504     { GLT_TIME_CONTROL,"Time Control" },\r
8505     { GLT_VARIANT,    "Variant" },\r
8506     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8507     { 0, 0 }\r
8508 };\r
8509 \r
8510 const char * GLT_FindItem( char id )\r
8511 {\r
8512     const char * result = 0;\r
8513 \r
8514     GLT_Item * list = GLT_ItemInfo;\r
8515 \r
8516     while( list->id != 0 ) {\r
8517         if( list->id == id ) {\r
8518             result = list->name;\r
8519             break;\r
8520         }\r
8521 \r
8522         list++;\r
8523     }\r
8524 \r
8525     return result;\r
8526 }\r
8527 \r
8528 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8529 {\r
8530     const char * name = GLT_FindItem( id );\r
8531 \r
8532     if( name != 0 ) {\r
8533         if( index >= 0 ) {\r
8534             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8535         }\r
8536         else {\r
8537             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8538         }\r
8539     }\r
8540 }\r
8541 \r
8542 void GLT_TagsToList( HWND hDlg, char * tags )\r
8543 {\r
8544     char * pc = tags;\r
8545 \r
8546     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8547 \r
8548     while( *pc ) {\r
8549         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8550         pc++;\r
8551     }\r
8552 \r
8553     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8554 \r
8555     pc = GLT_ALL_TAGS;\r
8556 \r
8557     while( *pc ) {\r
8558         if( strchr( tags, *pc ) == 0 ) {\r
8559             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8560         }\r
8561         pc++;\r
8562     }\r
8563 \r
8564     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8565 }\r
8566 \r
8567 char GLT_ListItemToTag( HWND hDlg, int index )\r
8568 {\r
8569     char result = '\0';\r
8570     char name[128];\r
8571 \r
8572     GLT_Item * list = GLT_ItemInfo;\r
8573 \r
8574     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8575         while( list->id != 0 ) {\r
8576             if( strcmp( list->name, name ) == 0 ) {\r
8577                 result = list->id;\r
8578                 break;\r
8579             }\r
8580 \r
8581             list++;\r
8582         }\r
8583     }\r
8584 \r
8585     return result;\r
8586 }\r
8587 \r
8588 void GLT_MoveSelection( HWND hDlg, int delta )\r
8589 {\r
8590     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8591     int idx2 = idx1 + delta;\r
8592     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8593 \r
8594     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8595         char buf[128];\r
8596 \r
8597         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8598         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8599         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8600         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8601     }\r
8602 }\r
8603 \r
8604 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8605 {\r
8606     static char glt[64];\r
8607     static char * lpUserGLT;\r
8608 \r
8609     switch( message )\r
8610     {\r
8611     case WM_INITDIALOG:\r
8612         lpUserGLT = (char *) lParam;\r
8613         \r
8614         strcpy( glt, lpUserGLT );\r
8615 \r
8616         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8617 \r
8618         /* Initialize list */\r
8619         GLT_TagsToList( hDlg, glt );\r
8620 \r
8621         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8622 \r
8623         break;\r
8624 \r
8625     case WM_COMMAND:\r
8626         switch( LOWORD(wParam) ) {\r
8627         case IDOK:\r
8628             {\r
8629                 char * pc = lpUserGLT;\r
8630                 int idx = 0;\r
8631                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8632                 char id;\r
8633 \r
8634                 do {\r
8635                     id = GLT_ListItemToTag( hDlg, idx );\r
8636 \r
8637                     *pc++ = id;\r
8638                     idx++;\r
8639                 } while( id != '\0' );\r
8640             }\r
8641             EndDialog( hDlg, 0 );\r
8642             return TRUE;\r
8643         case IDCANCEL:\r
8644             EndDialog( hDlg, 1 );\r
8645             return TRUE;\r
8646 \r
8647         case IDC_GLT_Default:\r
8648             strcpy( glt, GLT_DEFAULT_TAGS );\r
8649             GLT_TagsToList( hDlg, glt );\r
8650             return TRUE;\r
8651 \r
8652         case IDC_GLT_Restore:\r
8653             strcpy( glt, lpUserGLT );\r
8654             GLT_TagsToList( hDlg, glt );\r
8655             return TRUE;\r
8656 \r
8657         case IDC_GLT_Up:\r
8658             GLT_MoveSelection( hDlg, -1 );\r
8659             return TRUE;\r
8660 \r
8661         case IDC_GLT_Down:\r
8662             GLT_MoveSelection( hDlg, +1 );\r
8663             return TRUE;\r
8664         }\r
8665 \r
8666         break;\r
8667     }\r
8668 \r
8669     return FALSE;\r
8670 }\r
8671 \r
8672 int GameListOptions()\r
8673 {\r
8674     char glt[64];\r
8675     int result;\r
8676     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8677 \r
8678     strcpy( glt, appData.gameListTags );\r
8679 \r
8680     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8681 \r
8682     if( result == 0 ) {\r
8683         /* [AS] Memory leak here! */\r
8684         appData.gameListTags = strdup( glt ); \r
8685     }\r
8686 \r
8687     return result;\r
8688 }\r
8689 \r
8690 \r
8691 VOID\r
8692 DisplayIcsInteractionTitle(char *str)\r
8693 {\r
8694   char consoleTitle[MSG_SIZ];\r
8695 \r
8696   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8697   SetWindowText(hwndConsole, consoleTitle);\r
8698 }\r
8699 \r
8700 void\r
8701 DrawPosition(int fullRedraw, Board board)\r
8702 {\r
8703   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8704 }\r
8705 \r
8706 \r
8707 VOID\r
8708 ResetFrontEnd()\r
8709 {\r
8710   fromX = fromY = -1;\r
8711   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8712     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8713     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8714     dragInfo.lastpos = dragInfo.pos;\r
8715     dragInfo.start.x = dragInfo.start.y = -1;\r
8716     dragInfo.from = dragInfo.start;\r
8717     ReleaseCapture();\r
8718     DrawPosition(TRUE, NULL);\r
8719   }\r
8720 }\r
8721 \r
8722 \r
8723 VOID\r
8724 CommentPopUp(char *title, char *str)\r
8725 {\r
8726   HWND hwnd = GetActiveWindow();\r
8727   EitherCommentPopUp(0, title, str, FALSE);\r
8728   SetActiveWindow(hwnd);\r
8729 }\r
8730 \r
8731 VOID\r
8732 CommentPopDown(void)\r
8733 {\r
8734   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8735   if (commentDialog) {\r
8736     ShowWindow(commentDialog, SW_HIDE);\r
8737   }\r
8738   commentDialogUp = FALSE;\r
8739 }\r
8740 \r
8741 VOID\r
8742 EditCommentPopUp(int index, char *title, char *str)\r
8743 {\r
8744   EitherCommentPopUp(index, title, str, TRUE);\r
8745 }\r
8746 \r
8747 \r
8748 VOID\r
8749 RingBell()\r
8750 {\r
8751   MyPlaySound(&sounds[(int)SoundMove]);\r
8752 }\r
8753 \r
8754 VOID PlayIcsWinSound()\r
8755 {\r
8756   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8757 }\r
8758 \r
8759 VOID PlayIcsLossSound()\r
8760 {\r
8761   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8762 }\r
8763 \r
8764 VOID PlayIcsDrawSound()\r
8765 {\r
8766   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8767 }\r
8768 \r
8769 VOID PlayIcsUnfinishedSound()\r
8770 {\r
8771   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8772 }\r
8773 \r
8774 VOID\r
8775 PlayAlarmSound()\r
8776 {\r
8777   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8778 }\r
8779 \r
8780 \r
8781 VOID\r
8782 EchoOn()\r
8783 {\r
8784   HWND hInput;\r
8785   consoleEcho = TRUE;\r
8786   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8787   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8788   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8789 }\r
8790 \r
8791 \r
8792 VOID\r
8793 EchoOff()\r
8794 {\r
8795   CHARFORMAT cf;\r
8796   HWND hInput;\r
8797   consoleEcho = FALSE;\r
8798   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8799   /* This works OK: set text and background both to the same color */\r
8800   cf = consoleCF;\r
8801   cf.crTextColor = COLOR_ECHOOFF;\r
8802   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8803   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8804 }\r
8805 \r
8806 /* No Raw()...? */\r
8807 \r
8808 void Colorize(ColorClass cc, int continuation)\r
8809 {\r
8810   currentColorClass = cc;\r
8811   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8812   consoleCF.crTextColor = textAttribs[cc].color;\r
8813   consoleCF.dwEffects = textAttribs[cc].effects;\r
8814   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8815 }\r
8816 \r
8817 char *\r
8818 UserName()\r
8819 {\r
8820   static char buf[MSG_SIZ];\r
8821   DWORD bufsiz = MSG_SIZ;\r
8822 \r
8823   if (!GetUserName(buf, &bufsiz)) {\r
8824     /*DisplayError("Error getting user name", GetLastError());*/\r
8825     strcpy(buf, "User");\r
8826   }\r
8827   return buf;\r
8828 }\r
8829 \r
8830 char *\r
8831 HostName()\r
8832 {\r
8833   static char buf[MSG_SIZ];\r
8834   DWORD bufsiz = MSG_SIZ;\r
8835 \r
8836   if (!GetComputerName(buf, &bufsiz)) {\r
8837     /*DisplayError("Error getting host name", GetLastError());*/\r
8838     strcpy(buf, "Unknown");\r
8839   }\r
8840   return buf;\r
8841 }\r
8842 \r
8843 \r
8844 int\r
8845 ClockTimerRunning()\r
8846 {\r
8847   return clockTimerEvent != 0;\r
8848 }\r
8849 \r
8850 int\r
8851 StopClockTimer()\r
8852 {\r
8853   if (clockTimerEvent == 0) return FALSE;\r
8854   KillTimer(hwndMain, clockTimerEvent);\r
8855   clockTimerEvent = 0;\r
8856   return TRUE;\r
8857 }\r
8858 \r
8859 void\r
8860 StartClockTimer(long millisec)\r
8861 {\r
8862   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8863                              (UINT) millisec, NULL);\r
8864 }\r
8865 \r
8866 void\r
8867 DisplayWhiteClock(long timeRemaining, int highlight)\r
8868 {\r
8869   HDC hdc;\r
8870   hdc = GetDC(hwndMain);\r
8871   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8872 \r
8873   if (!IsIconic(hwndMain)) {\r
8874     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
8875   }\r
8876   if (highlight && iconCurrent == iconBlack) {\r
8877     iconCurrent = iconWhite;\r
8878     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8879     if (IsIconic(hwndMain)) {\r
8880       DrawIcon(hdc, 2, 2, iconCurrent);\r
8881     }\r
8882   }\r
8883   (void) ReleaseDC(hwndMain, hdc);\r
8884   if (hwndConsole)\r
8885     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8886 }\r
8887 \r
8888 void\r
8889 DisplayBlackClock(long timeRemaining, int highlight)\r
8890 {\r
8891   HDC hdc;\r
8892   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8893 \r
8894   hdc = GetDC(hwndMain);\r
8895   if (!IsIconic(hwndMain)) {\r
8896     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8897   }\r
8898   if (highlight && iconCurrent == iconWhite) {\r
8899     iconCurrent = iconBlack;\r
8900     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8901     if (IsIconic(hwndMain)) {\r
8902       DrawIcon(hdc, 2, 2, iconCurrent);\r
8903     }\r
8904   }\r
8905   (void) ReleaseDC(hwndMain, hdc);\r
8906   if (hwndConsole)\r
8907     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8908 }\r
8909 \r
8910 \r
8911 int\r
8912 LoadGameTimerRunning()\r
8913 {\r
8914   return loadGameTimerEvent != 0;\r
8915 }\r
8916 \r
8917 int\r
8918 StopLoadGameTimer()\r
8919 {\r
8920   if (loadGameTimerEvent == 0) return FALSE;\r
8921   KillTimer(hwndMain, loadGameTimerEvent);\r
8922   loadGameTimerEvent = 0;\r
8923   return TRUE;\r
8924 }\r
8925 \r
8926 void\r
8927 StartLoadGameTimer(long millisec)\r
8928 {\r
8929   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8930                                 (UINT) millisec, NULL);\r
8931 }\r
8932 \r
8933 void\r
8934 AutoSaveGame()\r
8935 {\r
8936   char *defName;\r
8937   FILE *f;\r
8938   char fileTitle[MSG_SIZ];\r
8939 \r
8940   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8941   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8942                      appData.oldSaveStyle ? "gam" : "pgn",\r
8943                      GAME_FILT, \r
8944                      "Save Game to File", NULL, fileTitle, NULL);\r
8945   if (f != NULL) {\r
8946     SaveGame(f, 0, "");\r
8947     fclose(f);\r
8948   }\r
8949 }\r
8950 \r
8951 \r
8952 void\r
8953 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8954 {\r
8955   if (delayedTimerEvent != 0) {\r
8956     if (appData.debugMode) {\r
8957       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8958     }\r
8959     KillTimer(hwndMain, delayedTimerEvent);\r
8960     delayedTimerEvent = 0;\r
8961     delayedTimerCallback();\r
8962   }\r
8963   delayedTimerCallback = cb;\r
8964   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8965                                 (UINT) millisec, NULL);\r
8966 }\r
8967 \r
8968 DelayedEventCallback\r
8969 GetDelayedEvent()\r
8970 {\r
8971   if (delayedTimerEvent) {\r
8972     return delayedTimerCallback;\r
8973   } else {\r
8974     return NULL;\r
8975   }\r
8976 }\r
8977 \r
8978 void\r
8979 CancelDelayedEvent()\r
8980 {\r
8981   if (delayedTimerEvent) {\r
8982     KillTimer(hwndMain, delayedTimerEvent);\r
8983     delayedTimerEvent = 0;\r
8984   }\r
8985 }\r
8986 \r
8987 /* Start a child process running the given program.\r
8988    The process's standard output can be read from "from", and its\r
8989    standard input can be written to "to".\r
8990    Exit with fatal error if anything goes wrong.\r
8991    Returns an opaque pointer that can be used to destroy the process\r
8992    later.\r
8993 */\r
8994 int\r
8995 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8996 {\r
8997 #define BUFSIZE 4096\r
8998 \r
8999   HANDLE hChildStdinRd, hChildStdinWr,\r
9000     hChildStdoutRd, hChildStdoutWr;\r
9001   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9002   SECURITY_ATTRIBUTES saAttr;\r
9003   BOOL fSuccess;\r
9004   PROCESS_INFORMATION piProcInfo;\r
9005   STARTUPINFO siStartInfo;\r
9006   ChildProc *cp;\r
9007   char buf[MSG_SIZ];\r
9008   DWORD err;\r
9009 \r
9010   if (appData.debugMode) {\r
9011     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9012   }\r
9013 \r
9014   *pr = NoProc;\r
9015 \r
9016   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9017   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9018   saAttr.bInheritHandle = TRUE;\r
9019   saAttr.lpSecurityDescriptor = NULL;\r
9020 \r
9021   /*\r
9022    * The steps for redirecting child's STDOUT:\r
9023    *     1. Create anonymous pipe to be STDOUT for child.\r
9024    *     2. Create a noninheritable duplicate of read handle,\r
9025    *         and close the inheritable read handle.\r
9026    */\r
9027 \r
9028   /* Create a pipe for the child's STDOUT. */\r
9029   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9030     return GetLastError();\r
9031   }\r
9032 \r
9033   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9034   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9035                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9036                              FALSE,     /* not inherited */\r
9037                              DUPLICATE_SAME_ACCESS);\r
9038   if (! fSuccess) {\r
9039     return GetLastError();\r
9040   }\r
9041   CloseHandle(hChildStdoutRd);\r
9042 \r
9043   /*\r
9044    * The steps for redirecting child's STDIN:\r
9045    *     1. Create anonymous pipe to be STDIN for child.\r
9046    *     2. Create a noninheritable duplicate of write handle,\r
9047    *         and close the inheritable write handle.\r
9048    */\r
9049 \r
9050   /* Create a pipe for the child's STDIN. */\r
9051   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9052     return GetLastError();\r
9053   }\r
9054 \r
9055   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9056   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9057                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9058                              FALSE,     /* not inherited */\r
9059                              DUPLICATE_SAME_ACCESS);\r
9060   if (! fSuccess) {\r
9061     return GetLastError();\r
9062   }\r
9063   CloseHandle(hChildStdinWr);\r
9064 \r
9065   /* Arrange to (1) look in dir for the child .exe file, and\r
9066    * (2) have dir be the child's working directory.  Interpret\r
9067    * dir relative to the directory WinBoard loaded from. */\r
9068   GetCurrentDirectory(MSG_SIZ, buf);\r
9069   SetCurrentDirectory(installDir);\r
9070   SetCurrentDirectory(dir);\r
9071 \r
9072   /* Now create the child process. */\r
9073 \r
9074   siStartInfo.cb = sizeof(STARTUPINFO);\r
9075   siStartInfo.lpReserved = NULL;\r
9076   siStartInfo.lpDesktop = NULL;\r
9077   siStartInfo.lpTitle = NULL;\r
9078   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9079   siStartInfo.cbReserved2 = 0;\r
9080   siStartInfo.lpReserved2 = NULL;\r
9081   siStartInfo.hStdInput = hChildStdinRd;\r
9082   siStartInfo.hStdOutput = hChildStdoutWr;\r
9083   siStartInfo.hStdError = hChildStdoutWr;\r
9084 \r
9085   fSuccess = CreateProcess(NULL,\r
9086                            cmdLine,        /* command line */\r
9087                            NULL,           /* process security attributes */\r
9088                            NULL,           /* primary thread security attrs */\r
9089                            TRUE,           /* handles are inherited */\r
9090                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9091                            NULL,           /* use parent's environment */\r
9092                            NULL,\r
9093                            &siStartInfo, /* STARTUPINFO pointer */\r
9094                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9095 \r
9096   err = GetLastError();\r
9097   SetCurrentDirectory(buf); /* return to prev directory */\r
9098   if (! fSuccess) {\r
9099     return err;\r
9100   }\r
9101 \r
9102   /* Close the handles we don't need in the parent */\r
9103   CloseHandle(piProcInfo.hThread);\r
9104   CloseHandle(hChildStdinRd);\r
9105   CloseHandle(hChildStdoutWr);\r
9106 \r
9107   /* Prepare return value */\r
9108   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9109   cp->kind = CPReal;\r
9110   cp->hProcess = piProcInfo.hProcess;\r
9111   cp->pid = piProcInfo.dwProcessId;\r
9112   cp->hFrom = hChildStdoutRdDup;\r
9113   cp->hTo = hChildStdinWrDup;\r
9114 \r
9115   *pr = (void *) cp;\r
9116 \r
9117   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9118      2000 where engines sometimes don't see the initial command(s)\r
9119      from WinBoard and hang.  I don't understand how that can happen,\r
9120      but the Sleep is harmless, so I've put it in.  Others have also\r
9121      reported what may be the same problem, so hopefully this will fix\r
9122      it for them too.  */\r
9123   Sleep(500);\r
9124 \r
9125   return NO_ERROR;\r
9126 }\r
9127 \r
9128 \r
9129 void\r
9130 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9131 {\r
9132   ChildProc *cp;\r
9133 \r
9134   cp = (ChildProc *) pr;\r
9135   if (cp == NULL) return;\r
9136 \r
9137   switch (cp->kind) {\r
9138   case CPReal:\r
9139     /* TerminateProcess is considered harmful, so... */\r
9140     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9141     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9142     /* The following doesn't work because the chess program\r
9143        doesn't "have the same console" as WinBoard.  Maybe\r
9144        we could arrange for this even though neither WinBoard\r
9145        nor the chess program uses a console for stdio? */\r
9146     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9147 \r
9148     /* [AS] Special termination modes for misbehaving programs... */\r
9149     if( signal == 9 ) {\r
9150         if ( appData.debugMode) {\r
9151             fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
9152         }\r
9153 \r
9154         TerminateProcess( cp->hProcess, 0 );\r
9155     }\r
9156     else if( signal == 10 ) {\r
9157         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9158 \r
9159         if( dw != WAIT_OBJECT_0 ) {\r
9160             if ( appData.debugMode) {\r
9161                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
9162             }\r
9163 \r
9164             TerminateProcess( cp->hProcess, 0 );\r
9165         }\r
9166     }\r
9167 \r
9168     CloseHandle(cp->hProcess);\r
9169     break;\r
9170 \r
9171   case CPComm:\r
9172     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9173     break;\r
9174 \r
9175   case CPSock:\r
9176     closesocket(cp->sock);\r
9177     WSACleanup();\r
9178     break;\r
9179 \r
9180   case CPRcmd:\r
9181     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9182     closesocket(cp->sock);\r
9183     closesocket(cp->sock2);\r
9184     WSACleanup();\r
9185     break;\r
9186   }\r
9187   free(cp);\r
9188 }\r
9189 \r
9190 void\r
9191 InterruptChildProcess(ProcRef pr)\r
9192 {\r
9193   ChildProc *cp;\r
9194 \r
9195   cp = (ChildProc *) pr;\r
9196   if (cp == NULL) return;\r
9197   switch (cp->kind) {\r
9198   case CPReal:\r
9199     /* The following doesn't work because the chess program\r
9200        doesn't "have the same console" as WinBoard.  Maybe\r
9201        we could arrange for this even though neither WinBoard\r
9202        nor the chess program uses a console for stdio */\r
9203     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9204     break;\r
9205 \r
9206   case CPComm:\r
9207   case CPSock:\r
9208     /* Can't interrupt */\r
9209     break;\r
9210 \r
9211   case CPRcmd:\r
9212     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9213     break;\r
9214   }\r
9215 }\r
9216 \r
9217 \r
9218 int\r
9219 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9220 {\r
9221   char cmdLine[MSG_SIZ];\r
9222 \r
9223   if (port[0] == NULLCHAR) {\r
9224     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9225   } else {\r
9226     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9227   }\r
9228   return StartChildProcess(cmdLine, "", pr);\r
9229 }\r
9230 \r
9231 \r
9232 /* Code to open TCP sockets */\r
9233 \r
9234 int\r
9235 OpenTCP(char *host, char *port, ProcRef *pr)\r
9236 {\r
9237   ChildProc *cp;\r
9238   int err;\r
9239   SOCKET s;\r
9240   struct sockaddr_in sa, mysa;\r
9241   struct hostent FAR *hp;\r
9242   unsigned short uport;\r
9243   WORD wVersionRequested;\r
9244   WSADATA wsaData;\r
9245 \r
9246   /* Initialize socket DLL */\r
9247   wVersionRequested = MAKEWORD(1, 1);\r
9248   err = WSAStartup(wVersionRequested, &wsaData);\r
9249   if (err != 0) return err;\r
9250 \r
9251   /* Make socket */\r
9252   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9253     err = WSAGetLastError();\r
9254     WSACleanup();\r
9255     return err;\r
9256   }\r
9257 \r
9258   /* Bind local address using (mostly) don't-care values.\r
9259    */\r
9260   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9261   mysa.sin_family = AF_INET;\r
9262   mysa.sin_addr.s_addr = INADDR_ANY;\r
9263   uport = (unsigned short) 0;\r
9264   mysa.sin_port = htons(uport);\r
9265   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9266       == SOCKET_ERROR) {\r
9267     err = WSAGetLastError();\r
9268     WSACleanup();\r
9269     return err;\r
9270   }\r
9271 \r
9272   /* Resolve remote host name */\r
9273   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9274   if (!(hp = gethostbyname(host))) {\r
9275     unsigned int b0, b1, b2, b3;\r
9276 \r
9277     err = WSAGetLastError();\r
9278 \r
9279     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9280       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9281       hp->h_addrtype = AF_INET;\r
9282       hp->h_length = 4;\r
9283       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9284       hp->h_addr_list[0] = (char *) malloc(4);\r
9285       hp->h_addr_list[0][0] = (char) b0;\r
9286       hp->h_addr_list[0][1] = (char) b1;\r
9287       hp->h_addr_list[0][2] = (char) b2;\r
9288       hp->h_addr_list[0][3] = (char) b3;\r
9289     } else {\r
9290       WSACleanup();\r
9291       return err;\r
9292     }\r
9293   }\r
9294   sa.sin_family = hp->h_addrtype;\r
9295   uport = (unsigned short) atoi(port);\r
9296   sa.sin_port = htons(uport);\r
9297   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9298 \r
9299   /* Make connection */\r
9300   if (connect(s, (struct sockaddr *) &sa,\r
9301               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9302     err = WSAGetLastError();\r
9303     WSACleanup();\r
9304     return err;\r
9305   }\r
9306 \r
9307   /* Prepare return value */\r
9308   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9309   cp->kind = CPSock;\r
9310   cp->sock = s;\r
9311   *pr = (ProcRef *) cp;\r
9312 \r
9313   return NO_ERROR;\r
9314 }\r
9315 \r
9316 int\r
9317 OpenCommPort(char *name, ProcRef *pr)\r
9318 {\r
9319   HANDLE h;\r
9320   COMMTIMEOUTS ct;\r
9321   ChildProc *cp;\r
9322   char fullname[MSG_SIZ];\r
9323 \r
9324   if (*name != '\\')\r
9325     sprintf(fullname, "\\\\.\\%s", name);\r
9326   else\r
9327     strcpy(fullname, name);\r
9328 \r
9329   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9330                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9331   if (h == (HANDLE) -1) {\r
9332     return GetLastError();\r
9333   }\r
9334   hCommPort = h;\r
9335 \r
9336   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9337 \r
9338   /* Accumulate characters until a 100ms pause, then parse */\r
9339   ct.ReadIntervalTimeout = 100;\r
9340   ct.ReadTotalTimeoutMultiplier = 0;\r
9341   ct.ReadTotalTimeoutConstant = 0;\r
9342   ct.WriteTotalTimeoutMultiplier = 0;\r
9343   ct.WriteTotalTimeoutConstant = 0;\r
9344   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9345 \r
9346   /* Prepare return value */\r
9347   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9348   cp->kind = CPComm;\r
9349   cp->hFrom = h;\r
9350   cp->hTo = h;\r
9351   *pr = (ProcRef *) cp;\r
9352 \r
9353   return NO_ERROR;\r
9354 }\r
9355 \r
9356 int\r
9357 OpenLoopback(ProcRef *pr)\r
9358 {\r
9359   DisplayFatalError("Not implemented", 0, 1);\r
9360   return NO_ERROR;\r
9361 }\r
9362 \r
9363 \r
9364 int\r
9365 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9366 {\r
9367   ChildProc *cp;\r
9368   int err;\r
9369   SOCKET s, s2, s3;\r
9370   struct sockaddr_in sa, mysa;\r
9371   struct hostent FAR *hp;\r
9372   unsigned short uport;\r
9373   WORD wVersionRequested;\r
9374   WSADATA wsaData;\r
9375   int fromPort;\r
9376   char stderrPortStr[MSG_SIZ];\r
9377 \r
9378   /* Initialize socket DLL */\r
9379   wVersionRequested = MAKEWORD(1, 1);\r
9380   err = WSAStartup(wVersionRequested, &wsaData);\r
9381   if (err != 0) return err;\r
9382 \r
9383   /* Resolve remote host name */\r
9384   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9385   if (!(hp = gethostbyname(host))) {\r
9386     unsigned int b0, b1, b2, b3;\r
9387 \r
9388     err = WSAGetLastError();\r
9389 \r
9390     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9391       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9392       hp->h_addrtype = AF_INET;\r
9393       hp->h_length = 4;\r
9394       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9395       hp->h_addr_list[0] = (char *) malloc(4);\r
9396       hp->h_addr_list[0][0] = (char) b0;\r
9397       hp->h_addr_list[0][1] = (char) b1;\r
9398       hp->h_addr_list[0][2] = (char) b2;\r
9399       hp->h_addr_list[0][3] = (char) b3;\r
9400     } else {\r
9401       WSACleanup();\r
9402       return err;\r
9403     }\r
9404   }\r
9405   sa.sin_family = hp->h_addrtype;\r
9406   uport = (unsigned short) 514;\r
9407   sa.sin_port = htons(uport);\r
9408   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9409 \r
9410   /* Bind local socket to unused "privileged" port address\r
9411    */\r
9412   s = INVALID_SOCKET;\r
9413   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9414   mysa.sin_family = AF_INET;\r
9415   mysa.sin_addr.s_addr = INADDR_ANY;\r
9416   for (fromPort = 1023;; fromPort--) {\r
9417     if (fromPort < 0) {\r
9418       WSACleanup();\r
9419       return WSAEADDRINUSE;\r
9420     }\r
9421     if (s == INVALID_SOCKET) {\r
9422       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9423         err = WSAGetLastError();\r
9424         WSACleanup();\r
9425         return err;\r
9426       }\r
9427     }\r
9428     uport = (unsigned short) fromPort;\r
9429     mysa.sin_port = htons(uport);\r
9430     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9431         == SOCKET_ERROR) {\r
9432       err = WSAGetLastError();\r
9433       if (err == WSAEADDRINUSE) continue;\r
9434       WSACleanup();\r
9435       return err;\r
9436     }\r
9437     if (connect(s, (struct sockaddr *) &sa,\r
9438       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9439       err = WSAGetLastError();\r
9440       if (err == WSAEADDRINUSE) {\r
9441         closesocket(s);\r
9442         s = -1;\r
9443         continue;\r
9444       }\r
9445       WSACleanup();\r
9446       return err;\r
9447     }\r
9448     break;\r
9449   }\r
9450 \r
9451   /* Bind stderr local socket to unused "privileged" port address\r
9452    */\r
9453   s2 = INVALID_SOCKET;\r
9454   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9455   mysa.sin_family = AF_INET;\r
9456   mysa.sin_addr.s_addr = INADDR_ANY;\r
9457   for (fromPort = 1023;; fromPort--) {\r
9458     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9459     if (fromPort < 0) {\r
9460       (void) closesocket(s);\r
9461       WSACleanup();\r
9462       return WSAEADDRINUSE;\r
9463     }\r
9464     if (s2 == INVALID_SOCKET) {\r
9465       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9466         err = WSAGetLastError();\r
9467         closesocket(s);\r
9468         WSACleanup();\r
9469         return err;\r
9470       }\r
9471     }\r
9472     uport = (unsigned short) fromPort;\r
9473     mysa.sin_port = htons(uport);\r
9474     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9475         == SOCKET_ERROR) {\r
9476       err = WSAGetLastError();\r
9477       if (err == WSAEADDRINUSE) continue;\r
9478       (void) closesocket(s);\r
9479       WSACleanup();\r
9480       return err;\r
9481     }\r
9482     if (listen(s2, 1) == SOCKET_ERROR) {\r
9483       err = WSAGetLastError();\r
9484       if (err == WSAEADDRINUSE) {\r
9485         closesocket(s2);\r
9486         s2 = INVALID_SOCKET;\r
9487         continue;\r
9488       }\r
9489       (void) closesocket(s);\r
9490       (void) closesocket(s2);\r
9491       WSACleanup();\r
9492       return err;\r
9493     }\r
9494     break;\r
9495   }\r
9496   prevStderrPort = fromPort; // remember port used\r
9497   sprintf(stderrPortStr, "%d", fromPort);\r
9498 \r
9499   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9500     err = WSAGetLastError();\r
9501     (void) closesocket(s);\r
9502     (void) closesocket(s2);\r
9503     WSACleanup();\r
9504     return err;\r
9505   }\r
9506 \r
9507   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9508     err = WSAGetLastError();\r
9509     (void) closesocket(s);\r
9510     (void) closesocket(s2);\r
9511     WSACleanup();\r
9512     return err;\r
9513   }\r
9514   if (*user == NULLCHAR) user = UserName();\r
9515   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9516     err = WSAGetLastError();\r
9517     (void) closesocket(s);\r
9518     (void) closesocket(s2);\r
9519     WSACleanup();\r
9520     return err;\r
9521   }\r
9522   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9523     err = WSAGetLastError();\r
9524     (void) closesocket(s);\r
9525     (void) closesocket(s2);\r
9526     WSACleanup();\r
9527     return err;\r
9528   }\r
9529 \r
9530   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9531     err = WSAGetLastError();\r
9532     (void) closesocket(s);\r
9533     (void) closesocket(s2);\r
9534     WSACleanup();\r
9535     return err;\r
9536   }\r
9537   (void) closesocket(s2);  /* Stop listening */\r
9538 \r
9539   /* Prepare return value */\r
9540   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9541   cp->kind = CPRcmd;\r
9542   cp->sock = s;\r
9543   cp->sock2 = s3;\r
9544   *pr = (ProcRef *) cp;\r
9545 \r
9546   return NO_ERROR;\r
9547 }\r
9548 \r
9549 \r
9550 InputSourceRef\r
9551 AddInputSource(ProcRef pr, int lineByLine,\r
9552                InputCallback func, VOIDSTAR closure)\r
9553 {\r
9554   InputSource *is, *is2 = NULL;\r
9555   ChildProc *cp = (ChildProc *) pr;\r
9556 \r
9557   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9558   is->lineByLine = lineByLine;\r
9559   is->func = func;\r
9560   is->closure = closure;\r
9561   is->second = NULL;\r
9562   is->next = is->buf;\r
9563   if (pr == NoProc) {\r
9564     is->kind = CPReal;\r
9565     consoleInputSource = is;\r
9566   } else {\r
9567     is->kind = cp->kind;\r
9568     /* \r
9569         [AS] Try to avoid a race condition if the thread is given control too early:\r
9570         we create all threads suspended so that the is->hThread variable can be\r
9571         safely assigned, then let the threads start with ResumeThread.\r
9572     */\r
9573     switch (cp->kind) {\r
9574     case CPReal:\r
9575       is->hFile = cp->hFrom;\r
9576       cp->hFrom = NULL; /* now owned by InputThread */\r
9577       is->hThread =\r
9578         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9579                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9580       break;\r
9581 \r
9582     case CPComm:\r
9583       is->hFile = cp->hFrom;\r
9584       cp->hFrom = NULL; /* now owned by InputThread */\r
9585       is->hThread =\r
9586         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9587                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9588       break;\r
9589 \r
9590     case CPSock:\r
9591       is->sock = cp->sock;\r
9592       is->hThread =\r
9593         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9594                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9595       break;\r
9596 \r
9597     case CPRcmd:\r
9598       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9599       *is2 = *is;\r
9600       is->sock = cp->sock;\r
9601       is->second = is2;\r
9602       is2->sock = cp->sock2;\r
9603       is2->second = is2;\r
9604       is->hThread =\r
9605         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9606                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9607       is2->hThread =\r
9608         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9609                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9610       break;\r
9611     }\r
9612 \r
9613     if( is->hThread != NULL ) {\r
9614         ResumeThread( is->hThread );\r
9615     }\r
9616 \r
9617     if( is2 != NULL && is2->hThread != NULL ) {\r
9618         ResumeThread( is2->hThread );\r
9619     }\r
9620   }\r
9621 \r
9622   return (InputSourceRef) is;\r
9623 }\r
9624 \r
9625 void\r
9626 RemoveInputSource(InputSourceRef isr)\r
9627 {\r
9628   InputSource *is;\r
9629 \r
9630   is = (InputSource *) isr;\r
9631   is->hThread = NULL;  /* tell thread to stop */\r
9632   CloseHandle(is->hThread);\r
9633   if (is->second != NULL) {\r
9634     is->second->hThread = NULL;\r
9635     CloseHandle(is->second->hThread);\r
9636   }\r
9637 }\r
9638 \r
9639 \r
9640 int\r
9641 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9642 {\r
9643   DWORD dOutCount;\r
9644   int outCount = SOCKET_ERROR;\r
9645   ChildProc *cp = (ChildProc *) pr;\r
9646   static OVERLAPPED ovl;\r
9647 \r
9648   if (pr == NoProc) {\r
9649     ConsoleOutput(message, count, FALSE);\r
9650     return count;\r
9651   } \r
9652 \r
9653   if (ovl.hEvent == NULL) {\r
9654     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9655   }\r
9656   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9657 \r
9658   switch (cp->kind) {\r
9659   case CPSock:\r
9660   case CPRcmd:\r
9661     outCount = send(cp->sock, message, count, 0);\r
9662     if (outCount == SOCKET_ERROR) {\r
9663       *outError = WSAGetLastError();\r
9664     } else {\r
9665       *outError = NO_ERROR;\r
9666     }\r
9667     break;\r
9668 \r
9669   case CPReal:\r
9670     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9671                   &dOutCount, NULL)) {\r
9672       *outError = NO_ERROR;\r
9673       outCount = (int) dOutCount;\r
9674     } else {\r
9675       *outError = GetLastError();\r
9676     }\r
9677     break;\r
9678 \r
9679   case CPComm:\r
9680     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9681                             &dOutCount, &ovl);\r
9682     if (*outError == NO_ERROR) {\r
9683       outCount = (int) dOutCount;\r
9684     }\r
9685     break;\r
9686   }\r
9687   return outCount;\r
9688 }\r
9689 \r
9690 int\r
9691 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9692                        long msdelay)\r
9693 {\r
9694   /* Ignore delay, not implemented for WinBoard */\r
9695   return OutputToProcess(pr, message, count, outError);\r
9696 }\r
9697 \r
9698 \r
9699 void\r
9700 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9701                         char *buf, int count, int error)\r
9702 {\r
9703   DisplayFatalError("Not implemented", 0, 1);\r
9704 }\r
9705 \r
9706 /* see wgamelist.c for Game List functions */\r
9707 /* see wedittags.c for Edit Tags functions */\r
9708 \r
9709 \r
9710 VOID\r
9711 ICSInitScript()\r
9712 {\r
9713   FILE *f;\r
9714   char buf[MSG_SIZ];\r
9715   char *dummy;\r
9716 \r
9717   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9718     f = fopen(buf, "r");\r
9719     if (f != NULL) {\r
9720       ProcessICSInitScript(f);\r
9721       fclose(f);\r
9722     }\r
9723   }\r
9724 }\r
9725 \r
9726 \r
9727 VOID\r
9728 StartAnalysisClock()\r
9729 {\r
9730   if (analysisTimerEvent) return;\r
9731   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9732                                         (UINT) 2000, NULL);\r
9733 }\r
9734 \r
9735 LRESULT CALLBACK\r
9736 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9737 {\r
9738   static HANDLE hwndText;\r
9739   RECT rect;\r
9740   static int sizeX, sizeY;\r
9741   int newSizeX, newSizeY, flags;\r
9742   MINMAXINFO *mmi;\r
9743 \r
9744   switch (message) {\r
9745   case WM_INITDIALOG: /* message: initialize dialog box */\r
9746     /* Initialize the dialog items */\r
9747     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9748     SetWindowText(hDlg, analysisTitle);\r
9749     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9750     /* Size and position the dialog */\r
9751     if (!analysisDialog) {\r
9752       analysisDialog = hDlg;\r
9753       flags = SWP_NOZORDER;\r
9754       GetClientRect(hDlg, &rect);\r
9755       sizeX = rect.right;\r
9756       sizeY = rect.bottom;\r
9757       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9758           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9759         WINDOWPLACEMENT wp;\r
9760         EnsureOnScreen(&analysisX, &analysisY);\r
9761         wp.length = sizeof(WINDOWPLACEMENT);\r
9762         wp.flags = 0;\r
9763         wp.showCmd = SW_SHOW;\r
9764         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9765         wp.rcNormalPosition.left = analysisX;\r
9766         wp.rcNormalPosition.right = analysisX + analysisW;\r
9767         wp.rcNormalPosition.top = analysisY;\r
9768         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9769         SetWindowPlacement(hDlg, &wp);\r
9770 \r
9771         GetClientRect(hDlg, &rect);\r
9772         newSizeX = rect.right;\r
9773         newSizeY = rect.bottom;\r
9774         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9775                               newSizeX, newSizeY);\r
9776         sizeX = newSizeX;\r
9777         sizeY = newSizeY;\r
9778       }\r
9779     }\r
9780     return FALSE;\r
9781 \r
9782   case WM_COMMAND: /* message: received a command */\r
9783     switch (LOWORD(wParam)) {\r
9784     case IDCANCEL:\r
9785       EditGameEvent();\r
9786       return TRUE;\r
9787     default:\r
9788       break;\r
9789     }\r
9790     break;\r
9791 \r
9792   case WM_SIZE:\r
9793     newSizeX = LOWORD(lParam);\r
9794     newSizeY = HIWORD(lParam);\r
9795     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9796     sizeX = newSizeX;\r
9797     sizeY = newSizeY;\r
9798     break;\r
9799 \r
9800   case WM_GETMINMAXINFO:\r
9801     /* Prevent resizing window too small */\r
9802     mmi = (MINMAXINFO *) lParam;\r
9803     mmi->ptMinTrackSize.x = 100;\r
9804     mmi->ptMinTrackSize.y = 100;\r
9805     break;\r
9806   }\r
9807   return FALSE;\r
9808 }\r
9809 \r
9810 VOID\r
9811 AnalysisPopUp(char* title, char* str)\r
9812 {\r
9813   FARPROC lpProc;\r
9814   char *p, *q;\r
9815 \r
9816   /* [AS] */\r
9817   EngineOutputPopUp();\r
9818   return;\r
9819 \r
9820   if (str == NULL) str = "";\r
9821   p = (char *) malloc(2 * strlen(str) + 2);\r
9822   q = p;\r
9823   while (*str) {\r
9824     if (*str == '\n') *q++ = '\r';\r
9825     *q++ = *str++;\r
9826   }\r
9827   *q = NULLCHAR;\r
9828   if (analysisText != NULL) free(analysisText);\r
9829   analysisText = p;\r
9830 \r
9831   if (analysisDialog) {\r
9832     SetWindowText(analysisDialog, title);\r
9833     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9834     ShowWindow(analysisDialog, SW_SHOW);\r
9835   } else {\r
9836     analysisTitle = title;\r
9837     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9838     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9839                  hwndMain, (DLGPROC)lpProc);\r
9840     FreeProcInstance(lpProc);\r
9841   }\r
9842   analysisDialogUp = TRUE;  \r
9843 }\r
9844 \r
9845 VOID\r
9846 AnalysisPopDown()\r
9847 {\r
9848   if (analysisDialog) {\r
9849     ShowWindow(analysisDialog, SW_HIDE);\r
9850   }\r
9851   analysisDialogUp = FALSE;  \r
9852 }\r
9853 \r
9854 \r
9855 VOID\r
9856 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9857 {\r
9858   highlightInfo.sq[0].x = fromX;\r
9859   highlightInfo.sq[0].y = fromY;\r
9860   highlightInfo.sq[1].x = toX;\r
9861   highlightInfo.sq[1].y = toY;\r
9862 }\r
9863 \r
9864 VOID\r
9865 ClearHighlights()\r
9866 {\r
9867   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9868     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9869 }\r
9870 \r
9871 VOID\r
9872 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9873 {\r
9874   premoveHighlightInfo.sq[0].x = fromX;\r
9875   premoveHighlightInfo.sq[0].y = fromY;\r
9876   premoveHighlightInfo.sq[1].x = toX;\r
9877   premoveHighlightInfo.sq[1].y = toY;\r
9878 }\r
9879 \r
9880 VOID\r
9881 ClearPremoveHighlights()\r
9882 {\r
9883   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9884     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9885 }\r
9886 \r
9887 VOID\r
9888 ShutDownFrontEnd()\r
9889 {\r
9890   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9891   DeleteClipboardTempFiles();\r
9892 }\r
9893 \r
9894 void\r
9895 BoardToTop()\r
9896 {\r
9897     if (IsIconic(hwndMain))\r
9898       ShowWindow(hwndMain, SW_RESTORE);\r
9899 \r
9900     SetActiveWindow(hwndMain);\r
9901 }\r
9902 \r
9903 /*\r
9904  * Prototypes for animation support routines\r
9905  */\r
9906 static void ScreenSquare(int column, int row, POINT * pt);\r
9907 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9908      POINT frames[], int * nFrames);\r
9909 \r
9910 \r
9911 #define kFactor 4\r
9912 \r
9913 void\r
9914 AnimateMove(board, fromX, fromY, toX, toY)\r
9915      Board board;\r
9916      int fromX;\r
9917      int fromY;\r
9918      int toX;\r
9919      int toY;\r
9920 {\r
9921   ChessSquare piece;\r
9922   POINT start, finish, mid;\r
9923   POINT frames[kFactor * 2 + 1];\r
9924   int nFrames, n;\r
9925 \r
9926   if (!appData.animate) return;\r
9927   if (doingSizing) return;\r
9928   if (fromY < 0 || fromX < 0) return;\r
9929   piece = board[fromY][fromX];\r
9930   if (piece >= EmptySquare) return;\r
9931 \r
9932   ScreenSquare(fromX, fromY, &start);\r
9933   ScreenSquare(toX, toY, &finish);\r
9934 \r
9935   /* All pieces except knights move in straight line */\r
9936   if (piece != WhiteKnight && piece != BlackKnight) {\r
9937     mid.x = start.x + (finish.x - start.x) / 2;\r
9938     mid.y = start.y + (finish.y - start.y) / 2;\r
9939   } else {\r
9940     /* Knight: make diagonal movement then straight */\r
9941     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9942        mid.x = start.x + (finish.x - start.x) / 2;\r
9943        mid.y = finish.y;\r
9944      } else {\r
9945        mid.x = finish.x;\r
9946        mid.y = start.y + (finish.y - start.y) / 2;\r
9947      }\r
9948   }\r
9949   \r
9950   /* Don't use as many frames for very short moves */\r
9951   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9952     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9953   else\r
9954     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9955 \r
9956   animInfo.from.x = fromX;\r
9957   animInfo.from.y = fromY;\r
9958   animInfo.to.x = toX;\r
9959   animInfo.to.y = toY;\r
9960   animInfo.lastpos = start;\r
9961   animInfo.piece = piece;\r
9962   for (n = 0; n < nFrames; n++) {\r
9963     animInfo.pos = frames[n];\r
9964     DrawPosition(FALSE, NULL);\r
9965     animInfo.lastpos = animInfo.pos;\r
9966     Sleep(appData.animSpeed);\r
9967   }\r
9968   animInfo.pos = finish;\r
9969   DrawPosition(FALSE, NULL);\r
9970   animInfo.piece = EmptySquare;\r
9971 }\r
9972 \r
9973 /*      Convert board position to corner of screen rect and color       */\r
9974 \r
9975 static void\r
9976 ScreenSquare(column, row, pt)\r
9977      int column; int row; POINT * pt;\r
9978 {\r
9979   if (flipView) {\r
9980     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9981     pt->y = lineGap + row * (squareSize + lineGap);\r
9982   } else {\r
9983     pt->x = lineGap + column * (squareSize + lineGap);\r
9984     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9985   }\r
9986 }\r
9987 \r
9988 /*      Generate a series of frame coords from start->mid->finish.\r
9989         The movement rate doubles until the half way point is\r
9990         reached, then halves back down to the final destination,\r
9991         which gives a nice slow in/out effect. The algorithmn\r
9992         may seem to generate too many intermediates for short\r
9993         moves, but remember that the purpose is to attract the\r
9994         viewers attention to the piece about to be moved and\r
9995         then to where it ends up. Too few frames would be less\r
9996         noticeable.                                             */\r
9997 \r
9998 static void\r
9999 Tween(start, mid, finish, factor, frames, nFrames)\r
10000      POINT * start; POINT * mid;\r
10001      POINT * finish; int factor;\r
10002      POINT frames[]; int * nFrames;\r
10003 {\r
10004   int n, fraction = 1, count = 0;\r
10005 \r
10006   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10007   for (n = 0; n < factor; n++)\r
10008     fraction *= 2;\r
10009   for (n = 0; n < factor; n++) {\r
10010     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10011     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10012     count ++;\r
10013     fraction = fraction / 2;\r
10014   }\r
10015   \r
10016   /* Midpoint */\r
10017   frames[count] = *mid;\r
10018   count ++;\r
10019   \r
10020   /* Slow out, stepping 1/2, then 1/4, ... */\r
10021   fraction = 2;\r
10022   for (n = 0; n < factor; n++) {\r
10023     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10024     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10025     count ++;\r
10026     fraction = fraction * 2;\r
10027   }\r
10028   *nFrames = count;\r
10029 }\r
10030 \r
10031 void\r
10032 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10033 {\r
10034 #if 0\r
10035     char buf[256];\r
10036 \r
10037     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10038         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10039 \r
10040     OutputDebugString( buf );\r
10041 #endif\r
10042 \r
10043     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10044 \r
10045     EvalGraphSet( first, last, current, pvInfoList );\r
10046 }\r
10047 \r
10048 void SetProgramStats( FrontEndProgramStats * stats )\r
10049 {\r
10050 #if 0\r
10051     char buf[1024];\r
10052 \r
10053     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10054         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10055 \r
10056     OutputDebugString( buf );\r
10057 #endif\r
10058 \r
10059     EngineOutputUpdate( stats );\r
10060 }\r