Fix bug in display of logos
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P(());\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 \r
138 typedef struct { // [HGM] atomic\r
139   int fromX, fromY, toX, toY, radius;\r
140 } ExplodeInfo;\r
141 \r
142 static ExplodeInfo explodeInfo;\r
143 \r
144 /* Window class names */\r
145 char szAppName[] = "WinBoard";\r
146 char szConsoleName[] = "WBConsole";\r
147 \r
148 /* Title bar text */\r
149 char szTitle[] = "WinBoard";\r
150 char szConsoleTitle[] = "I C S Interaction";\r
151 \r
152 char *programName;\r
153 char *settingsFileName;\r
154 Boolean saveSettingsOnExit;\r
155 char installDir[MSG_SIZ];\r
156 int errorExitStatus;\r
157 \r
158 BoardSize boardSize;\r
159 Boolean chessProgram;\r
160 //static int boardX, boardY;\r
161 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
162 static int squareSize, lineGap, minorSize;\r
163 static int winW, winH;\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
165 static int logoHeight = 0;\r
166 static char messageText[MESSAGE_TEXT_MAX];\r
167 static int clockTimerEvent = 0;\r
168 static int loadGameTimerEvent = 0;\r
169 static int analysisTimerEvent = 0;\r
170 static DelayedEventCallback delayedTimerCallback;\r
171 static int delayedTimerEvent = 0;\r
172 static int buttonCount = 2;\r
173 char *icsTextMenuString;\r
174 char *icsNames;\r
175 char *firstChessProgramNames;\r
176 char *secondChessProgramNames;\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 Boolean alwaysOnTop = FALSE;\r
182 RECT boardRect;\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
184   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
185 HPALETTE hPal;\r
186 ColorClass currentColorClass;\r
187 \r
188 HWND hCommPort = NULL;    /* currently open comm port */\r
189 static HWND hwndPause;    /* pause button */\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,\r
192   blackSquareBrush, /* [HGM] for band between board and holdings */\r
193   explodeBrush,     /* [HGM] atomic */\r
194   markerBrush,      /* [HGM] markers */\r
195   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
198 static HPEN gridPen = NULL;\r
199 static HPEN highlightPen = NULL;\r
200 static HPEN premovePen = NULL;\r
201 static NPLOGPALETTE pLogPal;\r
202 static BOOL paletteChanged = FALSE;\r
203 static HICON iconWhite, iconBlack, iconCurrent;\r
204 static int doingSizing = FALSE;\r
205 static int lastSizing = 0;\r
206 static int prevStderrPort;\r
207 static HBITMAP userLogo;\r
208 \r
209 static HBITMAP liteBackTexture = NULL;\r
210 static HBITMAP darkBackTexture = NULL;\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int backTextureSquareSize = 0;\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
215 \r
216 #if __GNUC__ && !defined(_winmajor)\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
218 #else\r
219 #if defined(_winmajor)\r
220 #define oldDialog (_winmajor < 4)\r
221 #else\r
222 #define oldDialog 0\r
223 #endif\r
224 #endif\r
225 \r
226 typedef struct {\r
227   char *name;\r
228   int squareSize;\r
229   int lineGap;\r
230   int smallLayout;\r
231   int tinyLayout;\r
232   int cliWidth, cliHeight;\r
233 } SizeInfo;\r
234 \r
235 SizeInfo sizeInfo[] = \r
236 {\r
237   { "tiny",     21, 0, 1, 1, 0, 0 },\r
238   { "teeny",    25, 1, 1, 1, 0, 0 },\r
239   { "dinky",    29, 1, 1, 1, 0, 0 },\r
240   { "petite",   33, 1, 1, 1, 0, 0 },\r
241   { "slim",     37, 2, 1, 0, 0, 0 },\r
242   { "small",    40, 2, 1, 0, 0, 0 },\r
243   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
244   { "middling", 49, 2, 0, 0, 0, 0 },\r
245   { "average",  54, 2, 0, 0, 0, 0 },\r
246   { "moderate", 58, 3, 0, 0, 0, 0 },\r
247   { "medium",   64, 3, 0, 0, 0, 0 },\r
248   { "bulky",    72, 3, 0, 0, 0, 0 },\r
249   { "large",    80, 3, 0, 0, 0, 0 },\r
250   { "big",      87, 3, 0, 0, 0, 0 },\r
251   { "huge",     95, 3, 0, 0, 0, 0 },\r
252   { "giant",    108, 3, 0, 0, 0, 0 },\r
253   { "colossal", 116, 4, 0, 0, 0, 0 },\r
254   { "titanic",  129, 4, 0, 0, 0, 0 },\r
255   { NULL, 0, 0, 0, 0, 0, 0 }\r
256 };\r
257 \r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
260 {\r
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279 };\r
280 \r
281 MyFont *font[NUM_SIZES][NUM_FONTS];\r
282 \r
283 typedef struct {\r
284   char *label;\r
285   int id;\r
286   HWND hwnd;\r
287   WNDPROC wndproc;\r
288 } MyButtonDesc;\r
289 \r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
291 #define N_BUTTONS 5\r
292 \r
293 MyButtonDesc buttonDesc[N_BUTTONS] =\r
294 {\r
295   {"<<", IDM_ToStart, NULL, NULL},\r
296   {"<", IDM_Backward, NULL, NULL},\r
297   {"P", IDM_Pause, NULL, NULL},\r
298   {">", IDM_Forward, NULL, NULL},\r
299   {">>", IDM_ToEnd, NULL, NULL},\r
300 };\r
301 \r
302 int tinyLayout = 0, smallLayout = 0;\r
303 #define MENU_BAR_ITEMS 7\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
305   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
306   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
307 };\r
308 \r
309 \r
310 MySound sounds[(int)NSoundClasses];\r
311 MyTextAttribs textAttribs[(int)NColorClasses];\r
312 \r
313 MyColorizeAttribs colorizeAttribs[] = {\r
314   { (COLORREF)0, 0, "Shout Text" },\r
315   { (COLORREF)0, 0, "SShout/CShout" },\r
316   { (COLORREF)0, 0, "Channel 1 Text" },\r
317   { (COLORREF)0, 0, "Channel Text" },\r
318   { (COLORREF)0, 0, "Kibitz Text" },\r
319   { (COLORREF)0, 0, "Tell Text" },\r
320   { (COLORREF)0, 0, "Challenge Text" },\r
321   { (COLORREF)0, 0, "Request Text" },\r
322   { (COLORREF)0, 0, "Seek Text" },\r
323   { (COLORREF)0, 0, "Normal Text" },\r
324   { (COLORREF)0, 0, "None" }\r
325 };\r
326 \r
327 \r
328 \r
329 static char *commentTitle;\r
330 static char *commentText;\r
331 static int commentIndex;\r
332 static Boolean editComment = FALSE;\r
333 \r
334 \r
335 char errorTitle[MSG_SIZ];\r
336 char errorMessage[2*MSG_SIZ];\r
337 HWND errorDialog = NULL;\r
338 BOOLEAN moveErrorMessageUp = FALSE;\r
339 BOOLEAN consoleEcho = TRUE;\r
340 CHARFORMAT consoleCF;\r
341 COLORREF consoleBackgroundColor;\r
342 \r
343 char *programVersion;\r
344 \r
345 #define CPReal 1\r
346 #define CPComm 2\r
347 #define CPSock 3\r
348 #define CPRcmd 4\r
349 typedef int CPKind;\r
350 \r
351 typedef struct {\r
352   CPKind kind;\r
353   HANDLE hProcess;\r
354   DWORD pid;\r
355   HANDLE hTo;\r
356   HANDLE hFrom;\r
357   SOCKET sock;\r
358   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
359 } ChildProc;\r
360 \r
361 #define INPUT_SOURCE_BUF_SIZE 4096\r
362 \r
363 typedef struct _InputSource {\r
364   CPKind kind;\r
365   HANDLE hFile;\r
366   SOCKET sock;\r
367   int lineByLine;\r
368   HANDLE hThread;\r
369   DWORD id;\r
370   char buf[INPUT_SOURCE_BUF_SIZE];\r
371   char *next;\r
372   DWORD count;\r
373   int error;\r
374   InputCallback func;\r
375   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
376   VOIDSTAR closure;\r
377 } InputSource;\r
378 \r
379 InputSource *consoleInputSource;\r
380 \r
381 DCB dcb;\r
382 \r
383 /* forward */\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
385 VOID ConsoleCreate();\r
386 LRESULT CALLBACK\r
387   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);\r
391 LRESULT CALLBACK\r
392   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
394 void ParseIcsTextMenu(char *icsTextMenuString);\r
395 VOID PopUpMoveDialog(char firstchar);\r
396 VOID PopUpNameDialog(char firstchar);\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
398 \r
399 /* [AS] */\r
400 int NewGameFRC();\r
401 int GameListOptions();\r
402 \r
403 int dummy; // [HGM] for obsolete args\r
404 \r
405 HWND hwndMain = NULL;        /* root window*/\r
406 HWND hwndConsole = NULL;\r
407 HWND commentDialog = NULL;\r
408 HWND moveHistoryDialog = NULL;\r
409 HWND evalGraphDialog = NULL;\r
410 HWND engineOutputDialog = NULL;\r
411 HWND gameListDialog = NULL;\r
412 HWND editTagsDialog = NULL;\r
413 \r
414 int commentUp = FALSE;\r
415 \r
416 WindowPlacement wpMain;\r
417 WindowPlacement wpConsole;\r
418 WindowPlacement wpComment;\r
419 WindowPlacement wpMoveHistory;\r
420 WindowPlacement wpEvalGraph;\r
421 WindowPlacement wpEngineOutput;\r
422 WindowPlacement wpGameList;\r
423 WindowPlacement wpTags;\r
424 \r
425 VOID EngineOptionsPopup(); // [HGM] settings\r
426 \r
427 VOID GothicPopUp(char *title, VariantClass variant);\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 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
464 \r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
466 #ifdef JAWS\r
467 #include "jaws.c"\r
468 #else\r
469 #define JAWS_INIT\r
470 #define JAWS_ARGS\r
471 #define JAWS_ALT_INTERCEPT\r
472 #define JAWS_KB_NAVIGATION\r
473 #define JAWS_MENU_ITEMS\r
474 #define JAWS_SILENCE\r
475 #define JAWS_REPLAY\r
476 #define JAWS_ACCEL\r
477 #define JAWS_COPYRIGHT\r
478 #define JAWS_DELETE(X) X\r
479 #define SAYMACHINEMOVE()\r
480 #define SAY(X)\r
481 #endif\r
482 \r
483 /*---------------------------------------------------------------------------*\\r
484  *\r
485  * WinMain\r
486  *\r
487 \*---------------------------------------------------------------------------*/\r
488 \r
489 int APIENTRY\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
491         LPSTR lpCmdLine, int nCmdShow)\r
492 {\r
493   MSG msg;\r
494   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
495 //  INITCOMMONCONTROLSEX ex;\r
496 \r
497   debugFP = stderr;\r
498 \r
499   LoadLibrary("RICHED32.DLL");\r
500   consoleCF.cbSize = sizeof(CHARFORMAT);\r
501 \r
502   if (!InitApplication(hInstance)) {\r
503     return (FALSE);\r
504   }\r
505   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
506     return (FALSE);\r
507   }\r
508 \r
509   JAWS_INIT\r
510 \r
511 //  InitCommonControlsEx(&ex);\r
512   InitCommonControls();\r
513 \r
514   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
515   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
516   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
517 \r
518   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
519 \r
520   while (GetMessage(&msg, /* message structure */\r
521                     NULL, /* handle of window receiving the message */\r
522                     0,    /* lowest message to examine */\r
523                     0))   /* highest message to examine */\r
524     {\r
525 \r
526       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
527         // [HGM] navigate: switch between all windows with tab\r
528         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
529         int i, currentElement = 0;\r
530 \r
531         // first determine what element of the chain we come from (if any)\r
532         if(appData.icsActive) {\r
533             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
534             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
535         }\r
536         if(engineOutputDialog && EngineOutputIsUp()) {\r
537             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
538             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
539         }\r
540         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
541             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
542         }\r
543         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
544         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
545         if(msg.hwnd == e1)                 currentElement = 2; else\r
546         if(msg.hwnd == e2)                 currentElement = 3; else\r
547         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
548         if(msg.hwnd == mh)                currentElement = 4; else\r
549         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
550         if(msg.hwnd == hText)  currentElement = 5; else\r
551         if(msg.hwnd == hInput) currentElement = 6; else\r
552         for (i = 0; i < N_BUTTONS; i++) {\r
553             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
554         }\r
555 \r
556         // determine where to go to\r
557         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
558           do {\r
559             currentElement = (currentElement + direction) % 7;\r
560             switch(currentElement) {\r
561                 case 0:\r
562                   h = hwndMain; break; // passing this case always makes the loop exit\r
563                 case 1:\r
564                   h = buttonDesc[0].hwnd; break; // could be NULL\r
565                 case 2:\r
566                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
567                   h = e1; break;\r
568                 case 3:\r
569                   if(!EngineOutputIsUp()) continue;\r
570                   h = e2; break;\r
571                 case 4:\r
572                   if(!MoveHistoryIsUp()) continue;\r
573                   h = mh; break;\r
574 //              case 6: // input to eval graph does not seem to get here!\r
575 //                if(!EvalGraphIsUp()) continue;\r
576 //                h = evalGraphDialog; break;\r
577                 case 5:\r
578                   if(!appData.icsActive) continue;\r
579                   SAY("display");\r
580                   h = hText; break;\r
581                 case 6:\r
582                   if(!appData.icsActive) continue;\r
583                   SAY("input");\r
584                   h = hInput; break;\r
585             }\r
586           } while(h == 0);\r
587 \r
588           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
589           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
590           SetFocus(h);\r
591 \r
592           continue; // this message now has been processed\r
593         }\r
594       }\r
595 \r
596       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
597           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
598           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
599           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
600           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
601           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
602           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
603           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
604           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
605           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
606         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
607         for(i=0; i<MAX_CHAT; i++) \r
608             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
609                 done = 1; break;\r
610         }\r
611         if(done) continue; // [HGM] chat: end patch\r
612         TranslateMessage(&msg); /* Translates virtual key codes */\r
613         DispatchMessage(&msg);  /* Dispatches message to window */\r
614       }\r
615     }\r
616 \r
617 \r
618   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
619 }\r
620 \r
621 /*---------------------------------------------------------------------------*\\r
622  *\r
623  * Initialization functions\r
624  *\r
625 \*---------------------------------------------------------------------------*/\r
626 \r
627 void\r
628 SetUserLogo()\r
629 {   // update user logo if necessary\r
630     static char oldUserName[MSG_SIZ], *curName;\r
631 \r
632     if(appData.autoLogo) {\r
633           curName = UserName();\r
634           if(strcmp(curName, oldUserName)) {\r
635                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
636                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
637                 strcpy(oldUserName, curName);\r
638           }\r
639     }\r
640 }\r
641 \r
642 BOOL\r
643 InitApplication(HINSTANCE hInstance)\r
644 {\r
645   WNDCLASS wc;\r
646 \r
647   /* Fill in window class structure with parameters that describe the */\r
648   /* main window. */\r
649 \r
650   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
651   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
652   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
653   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
654   wc.hInstance     = hInstance;         /* Owner of this class */\r
655   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
656   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
657   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
658   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
659   wc.lpszClassName = szAppName;                 /* Name to register as */\r
660 \r
661   /* Register the window class and return success/failure code. */\r
662   if (!RegisterClass(&wc)) return FALSE;\r
663 \r
664   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
665   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
666   wc.cbClsExtra    = 0;\r
667   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
668   wc.hInstance     = hInstance;\r
669   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
670   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
671   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
672   wc.lpszMenuName  = NULL;\r
673   wc.lpszClassName = szConsoleName;\r
674 \r
675   if (!RegisterClass(&wc)) return FALSE;\r
676   return TRUE;\r
677 }\r
678 \r
679 \r
680 /* Set by InitInstance, used by EnsureOnScreen */\r
681 int screenHeight, screenWidth;\r
682 \r
683 void\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
685 {\r
686 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
687   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
688   if (*x > screenWidth - 32) *x = 0;\r
689   if (*y > screenHeight - 32) *y = 0;\r
690   if (*x < minX) *x = minX;\r
691   if (*y < minY) *y = minY;\r
692 }\r
693 \r
694 BOOL\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
696 {\r
697   HWND hwnd; /* Main window handle. */\r
698   int ibs;\r
699   WINDOWPLACEMENT wp;\r
700   char *filepart;\r
701 \r
702   hInst = hInstance;    /* Store instance handle in our global variable */\r
703   programName = szAppName;\r
704 \r
705   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
706     *filepart = NULLCHAR;\r
707   } else {\r
708     GetCurrentDirectory(MSG_SIZ, installDir);\r
709   }\r
710   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
711   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
712   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
713   /* xboard, and older WinBoards, controlled the move sound with the\r
714      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
715      always turn the option on (so that the backend will call us),\r
716      then let the user turn the sound off by setting it to silence if\r
717      desired.  To accommodate old winboard.ini files saved by old\r
718      versions of WinBoard, we also turn off the sound if the option\r
719      was initially set to false. [HGM] taken out of InitAppData */\r
720   if (!appData.ringBellAfterMoves) {\r
721     sounds[(int)SoundMove].name = strdup("");\r
722     appData.ringBellAfterMoves = TRUE;\r
723   }\r
724   if (appData.debugMode) {\r
725     debugFP = fopen(appData.nameOfDebugFile, "w");\r
726     setbuf(debugFP, NULL);\r
727   }\r
728 \r
729   InitBackEnd1();\r
730 \r
731 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
732 //  InitEngineUCI( installDir, &second );\r
733 \r
734   /* Create a main window for this application instance. */\r
735   hwnd = CreateWindow(szAppName, szTitle,\r
736                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
737                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
738                       NULL, NULL, hInstance, NULL);\r
739   hwndMain = hwnd;\r
740 \r
741   /* If window could not be created, return "failure" */\r
742   if (!hwnd) {\r
743     return (FALSE);\r
744   }\r
745 \r
746   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
747   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
748       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
749 \r
750       if (first.programLogo == NULL && appData.debugMode) {\r
751           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
752       }\r
753   } else if(appData.autoLogo) {\r
754       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
755         char buf[MSG_SIZ];\r
756         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
757         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
758       }\r
759   }\r
760 \r
761   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
762       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
763 \r
764       if (second.programLogo == NULL && appData.debugMode) {\r
765           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
766       }\r
767   } else if(appData.autoLogo) {\r
768       char buf[MSG_SIZ];\r
769       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
770         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
771         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
772       } else\r
773       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
774         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
775         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
776       }\r
777   }\r
778 \r
779   SetUserLogo();\r
780 \r
781   iconWhite = LoadIcon(hInstance, "icon_white");\r
782   iconBlack = LoadIcon(hInstance, "icon_black");\r
783   iconCurrent = iconWhite;\r
784   InitDrawingColors();\r
785   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
786   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
787   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
788     /* Compute window size for each board size, and use the largest\r
789        size that fits on this screen as the default. */\r
790     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
791     if (boardSize == (BoardSize)-1 &&\r
792         winH <= screenHeight\r
793            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
794         && winW <= screenWidth) {\r
795       boardSize = (BoardSize)ibs;\r
796     }\r
797   }\r
798 \r
799   InitDrawingSizes(boardSize, 0);\r
800   InitMenuChecks();\r
801   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
802 \r
803   /* [AS] Load textures if specified */\r
804   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
805   \r
806   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
807       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
808       liteBackTextureMode = appData.liteBackTextureMode;\r
809 \r
810       if (liteBackTexture == NULL && appData.debugMode) {\r
811           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
812       }\r
813   }\r
814   \r
815   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
816       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
817       darkBackTextureMode = appData.darkBackTextureMode;\r
818 \r
819       if (darkBackTexture == NULL && appData.debugMode) {\r
820           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
821       }\r
822   }\r
823 \r
824   mysrandom( (unsigned) time(NULL) );\r
825 \r
826   /* [AS] Restore layout */\r
827   if( wpMoveHistory.visible ) {\r
828       MoveHistoryPopUp();\r
829   }\r
830 \r
831   if( wpEvalGraph.visible ) {\r
832       EvalGraphPopUp();\r
833   }\r
834 \r
835   if( wpEngineOutput.visible ) {\r
836       EngineOutputPopUp();\r
837   }\r
838 \r
839   InitBackEnd2();\r
840 \r
841   /* Make the window visible; update its client area; and return "success" */\r
842   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
843   wp.length = sizeof(WINDOWPLACEMENT);\r
844   wp.flags = 0;\r
845   wp.showCmd = nCmdShow;\r
846   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
847   wp.rcNormalPosition.left = wpMain.x;\r
848   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
849   wp.rcNormalPosition.top = wpMain.y;\r
850   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
851   SetWindowPlacement(hwndMain, &wp);\r
852 \r
853   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
854                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
855 \r
856   if (hwndConsole) {\r
857 #if AOT_CONSOLE\r
858     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
859                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
860 #endif\r
861     ShowWindow(hwndConsole, nCmdShow);\r
862   }\r
863   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
864   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
865 \r
866   return TRUE;\r
867 \r
868 }\r
869 \r
870 VOID\r
871 InitMenuChecks()\r
872 {\r
873   HMENU hmenu = GetMenu(hwndMain);\r
874 \r
875   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
876                         MF_BYCOMMAND|((appData.icsActive &&\r
877                                        *appData.icsCommPort != NULLCHAR) ?\r
878                                       MF_ENABLED : MF_GRAYED));\r
879   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
880                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
881                                      MF_CHECKED : MF_UNCHECKED));\r
882 }\r
883 \r
884 //---------------------------------------------------------------------------------------------------------\r
885 \r
886 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
887 #define XBOARD FALSE\r
888 \r
889 #define OPTCHAR "/"\r
890 #define SEPCHAR "="\r
891 \r
892 #include "args.h"\r
893 \r
894 // front-end part of option handling\r
895 \r
896 VOID\r
897 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
898 {\r
899   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
900   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
901   DeleteDC(hdc);\r
902   lf->lfWidth = 0;\r
903   lf->lfEscapement = 0;\r
904   lf->lfOrientation = 0;\r
905   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
906   lf->lfItalic = mfp->italic;\r
907   lf->lfUnderline = mfp->underline;\r
908   lf->lfStrikeOut = mfp->strikeout;\r
909   lf->lfCharSet = mfp->charset;\r
910   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
911   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
912   lf->lfQuality = DEFAULT_QUALITY;\r
913   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
914   strcpy(lf->lfFaceName, mfp->faceName);\r
915 }\r
916 \r
917 void\r
918 CreateFontInMF(MyFont *mf)\r
919\r
920   LFfromMFP(&mf->lf, &mf->mfp);\r
921   if (mf->hf) DeleteObject(mf->hf);\r
922   mf->hf = CreateFontIndirect(&mf->lf);\r
923 }\r
924 \r
925 // [HGM] This platform-dependent table provides the location for storing the color info\r
926 void *\r
927 colorVariable[] = {\r
928   &whitePieceColor, \r
929   &blackPieceColor, \r
930   &lightSquareColor,\r
931   &darkSquareColor, \r
932   &highlightSquareColor,\r
933   &premoveHighlightColor,\r
934   NULL,\r
935   &consoleBackgroundColor,\r
936   &appData.fontForeColorWhite,\r
937   &appData.fontBackColorWhite,\r
938   &appData.fontForeColorBlack,\r
939   &appData.fontBackColorBlack,\r
940   &appData.evalHistColorWhite,\r
941   &appData.evalHistColorBlack,\r
942   &appData.highlightArrowColor,\r
943 };\r
944 \r
945 /* Command line font name parser.  NULL name means do nothing.\r
946    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
947    For backward compatibility, syntax without the colon is also\r
948    accepted, but font names with digits in them won't work in that case.\r
949 */\r
950 VOID\r
951 ParseFontName(char *name, MyFontParams *mfp)\r
952 {\r
953   char *p, *q;\r
954   if (name == NULL) return;\r
955   p = name;\r
956   q = strchr(p, ':');\r
957   if (q) {\r
958     if (q - p >= sizeof(mfp->faceName))\r
959       ExitArgError("Font name too long:", name);\r
960     memcpy(mfp->faceName, p, q - p);\r
961     mfp->faceName[q - p] = NULLCHAR;\r
962     p = q + 1;\r
963   } else {\r
964     q = mfp->faceName;\r
965     while (*p && !isdigit(*p)) {\r
966       *q++ = *p++;\r
967       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
968         ExitArgError("Font name too long:", name);\r
969     }\r
970     while (q > mfp->faceName && q[-1] == ' ') q--;\r
971     *q = NULLCHAR;\r
972   }\r
973   if (!*p) ExitArgError("Font point size missing:", name);\r
974   mfp->pointSize = (float) atof(p);\r
975   mfp->bold = (strchr(p, 'b') != NULL);\r
976   mfp->italic = (strchr(p, 'i') != NULL);\r
977   mfp->underline = (strchr(p, 'u') != NULL);\r
978   mfp->strikeout = (strchr(p, 's') != NULL);\r
979   mfp->charset = DEFAULT_CHARSET;\r
980   q = strchr(p, 'c');\r
981   if (q)\r
982     mfp->charset = (BYTE) atoi(q+1);\r
983 }\r
984 \r
985 void\r
986 ParseFont(char *name, int number)\r
987 { // wrapper to shield back-end from 'font'\r
988   ParseFontName(name, &font[boardSize][number]->mfp);\r
989 }\r
990 \r
991 void\r
992 SetFontDefaults()\r
993 { // in WB  we have a 2D array of fonts; this initializes their description\r
994   int i, j;\r
995   /* Point font array elements to structures and\r
996      parse default font names */\r
997   for (i=0; i<NUM_FONTS; i++) {\r
998     for (j=0; j<NUM_SIZES; j++) {\r
999       font[j][i] = &fontRec[j][i];\r
1000       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1001     }\r
1002   }\r
1003 }\r
1004 \r
1005 void\r
1006 CreateFonts()\r
1007 { // here we create the actual fonts from the selected descriptions\r
1008   int i, j;\r
1009   for (i=0; i<NUM_FONTS; i++) {\r
1010     for (j=0; j<NUM_SIZES; j++) {\r
1011       CreateFontInMF(font[j][i]);\r
1012     }\r
1013   }\r
1014 }\r
1015 /* Color name parser.\r
1016    X version accepts X color names, but this one\r
1017    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1018 COLORREF\r
1019 ParseColorName(char *name)\r
1020 {\r
1021   int red, green, blue, count;\r
1022   char buf[MSG_SIZ];\r
1023 \r
1024   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1025   if (count != 3) {\r
1026     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1027       &red, &green, &blue);\r
1028   }\r
1029   if (count != 3) {\r
1030     sprintf(buf, "Can't parse color name %s", name);\r
1031     DisplayError(buf, 0);\r
1032     return RGB(0, 0, 0);\r
1033   }\r
1034   return PALETTERGB(red, green, blue);\r
1035 }\r
1036 \r
1037 void\r
1038 ParseColor(int n, char *name)\r
1039 { // for WinBoard the color is an int, which needs to be derived from the string\r
1040   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1041 }\r
1042 \r
1043 void\r
1044 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1045 {\r
1046   char *e = argValue;\r
1047   int eff = 0;\r
1048 \r
1049   while (*e) {\r
1050     if (*e == 'b')      eff |= CFE_BOLD;\r
1051     else if (*e == 'i') eff |= CFE_ITALIC;\r
1052     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1053     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1054     else if (*e == '#' || isdigit(*e)) break;\r
1055     e++;\r
1056   }\r
1057   *effects = eff;\r
1058   *color   = ParseColorName(e);\r
1059 }\r
1060 \r
1061 void\r
1062 ParseTextAttribs(ColorClass cc, char *s)\r
1063 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1064     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1065     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1066 }\r
1067 \r
1068 void\r
1069 ParseBoardSize(void *addr, char *name)\r
1070 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1071   BoardSize bs = SizeTiny;\r
1072   while (sizeInfo[bs].name != NULL) {\r
1073     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1074         *(BoardSize *)addr = bs;\r
1075         return;\r
1076     }\r
1077     bs++;\r
1078   }\r
1079   ExitArgError("Unrecognized board size value", name);\r
1080 }\r
1081 \r
1082 void\r
1083 LoadAllSounds()\r
1084 { // [HGM] import name from appData first\r
1085   ColorClass cc;\r
1086   SoundClass sc;\r
1087   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1088     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1089     textAttribs[cc].sound.data = NULL;\r
1090     MyLoadSound(&textAttribs[cc].sound);\r
1091   }\r
1092   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1093     textAttribs[cc].sound.name = strdup("");\r
1094     textAttribs[cc].sound.data = NULL;\r
1095   }\r
1096   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1097     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1098     sounds[sc].data = NULL;\r
1099     MyLoadSound(&sounds[sc]);\r
1100   }\r
1101 }\r
1102 \r
1103 void\r
1104 SetCommPortDefaults()\r
1105 {\r
1106    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1107   dcb.DCBlength = sizeof(DCB);\r
1108   dcb.BaudRate = 9600;\r
1109   dcb.fBinary = TRUE;\r
1110   dcb.fParity = FALSE;\r
1111   dcb.fOutxCtsFlow = FALSE;\r
1112   dcb.fOutxDsrFlow = FALSE;\r
1113   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1114   dcb.fDsrSensitivity = FALSE;\r
1115   dcb.fTXContinueOnXoff = TRUE;\r
1116   dcb.fOutX = FALSE;\r
1117   dcb.fInX = FALSE;\r
1118   dcb.fNull = FALSE;\r
1119   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1120   dcb.fAbortOnError = FALSE;\r
1121   dcb.ByteSize = 7;\r
1122   dcb.Parity = SPACEPARITY;\r
1123   dcb.StopBits = ONESTOPBIT;\r
1124 }\r
1125 \r
1126 // [HGM] args: these three cases taken out to stay in front-end\r
1127 void\r
1128 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1129 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1130         // while the curent board size determines the element. This system should be ported to XBoard.\r
1131         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1132         int bs;\r
1133         for (bs=0; bs<NUM_SIZES; bs++) {\r
1134           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1135           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1136           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1137             ad->argName, mfp->faceName, mfp->pointSize,\r
1138             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1139             mfp->bold ? "b" : "",\r
1140             mfp->italic ? "i" : "",\r
1141             mfp->underline ? "u" : "",\r
1142             mfp->strikeout ? "s" : "",\r
1143             (int)mfp->charset);\r
1144         }\r
1145       }\r
1146 \r
1147 void\r
1148 ExportSounds()\r
1149 { // [HGM] copy the names from the internal WB variables to appData\r
1150   ColorClass cc;\r
1151   SoundClass sc;\r
1152   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1153     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1154   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1155     (&appData.soundMove)[sc] = sounds[sc].name;\r
1156 }\r
1157 \r
1158 void\r
1159 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1160 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1161         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1162         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1163           (ta->effects & CFE_BOLD) ? "b" : "",\r
1164           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1165           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1166           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1167           (ta->effects) ? " " : "",\r
1168           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1169       }\r
1170 \r
1171 void\r
1172 SaveColor(FILE *f, ArgDescriptor *ad)\r
1173 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1174         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1175         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1176           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1177 }\r
1178 \r
1179 void\r
1180 SaveBoardSize(FILE *f, char *name, void *addr)\r
1181 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1182   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1183 }\r
1184 \r
1185 void\r
1186 ParseCommPortSettings(char *s)\r
1187 { // wrapper to keep dcb from back-end\r
1188   ParseCommSettings(s, &dcb);\r
1189 }\r
1190 \r
1191 void\r
1192 GetWindowCoords()\r
1193 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1194   GetActualPlacement(hwndMain, &wpMain);\r
1195   GetActualPlacement(hwndConsole, &wpConsole);\r
1196   GetActualPlacement(commentDialog, &wpComment);\r
1197   GetActualPlacement(editTagsDialog, &wpTags);\r
1198   GetActualPlacement(gameListDialog, &wpGameList);\r
1199   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1200   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1201   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1202 }\r
1203 \r
1204 void\r
1205 PrintCommPortSettings(FILE *f, char *name)\r
1206 { // wrapper to shield back-end from DCB\r
1207       PrintCommSettings(f, name, &dcb);\r
1208 }\r
1209 \r
1210 int\r
1211 MySearchPath(char *installDir, char *name, char *fullname)\r
1212 {\r
1213   char *dummy;\r
1214   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1215 }\r
1216 \r
1217 int\r
1218 MyGetFullPathName(char *name, char *fullname)\r
1219 {\r
1220   char *dummy;\r
1221   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1222 }\r
1223 \r
1224 int\r
1225 MainWindowUp()\r
1226 { // [HGM] args: allows testing if main window is realized from back-end\r
1227   return hwndMain != NULL;\r
1228 }\r
1229 \r
1230 void\r
1231 PopUpStartupDialog()\r
1232 {\r
1233     FARPROC lpProc;\r
1234     \r
1235     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1236     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1237     FreeProcInstance(lpProc);\r
1238 }\r
1239 \r
1240 /*---------------------------------------------------------------------------*\\r
1241  *\r
1242  * GDI board drawing routines\r
1243  *\r
1244 \*---------------------------------------------------------------------------*/\r
1245 \r
1246 /* [AS] Draw square using background texture */\r
1247 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1248 {\r
1249     XFORM   x;\r
1250 \r
1251     if( mode == 0 ) {\r
1252         return; /* Should never happen! */\r
1253     }\r
1254 \r
1255     SetGraphicsMode( dst, GM_ADVANCED );\r
1256 \r
1257     switch( mode ) {\r
1258     case 1:\r
1259         /* Identity */\r
1260         break;\r
1261     case 2:\r
1262         /* X reflection */\r
1263         x.eM11 = -1.0;\r
1264         x.eM12 = 0;\r
1265         x.eM21 = 0;\r
1266         x.eM22 = 1.0;\r
1267         x.eDx = (FLOAT) dw + dx - 1;\r
1268         x.eDy = 0;\r
1269         dx = 0;\r
1270         SetWorldTransform( dst, &x );\r
1271         break;\r
1272     case 3:\r
1273         /* Y reflection */\r
1274         x.eM11 = 1.0;\r
1275         x.eM12 = 0;\r
1276         x.eM21 = 0;\r
1277         x.eM22 = -1.0;\r
1278         x.eDx = 0;\r
1279         x.eDy = (FLOAT) dh + dy - 1;\r
1280         dy = 0;\r
1281         SetWorldTransform( dst, &x );\r
1282         break;\r
1283     case 4:\r
1284         /* X/Y flip */\r
1285         x.eM11 = 0;\r
1286         x.eM12 = 1.0;\r
1287         x.eM21 = 1.0;\r
1288         x.eM22 = 0;\r
1289         x.eDx = (FLOAT) dx;\r
1290         x.eDy = (FLOAT) dy;\r
1291         dx = 0;\r
1292         dy = 0;\r
1293         SetWorldTransform( dst, &x );\r
1294         break;\r
1295     }\r
1296 \r
1297     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1298 \r
1299     x.eM11 = 1.0;\r
1300     x.eM12 = 0;\r
1301     x.eM21 = 0;\r
1302     x.eM22 = 1.0;\r
1303     x.eDx = 0;\r
1304     x.eDy = 0;\r
1305     SetWorldTransform( dst, &x );\r
1306 \r
1307     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1308 }\r
1309 \r
1310 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1311 enum {\r
1312     PM_WP = (int) WhitePawn, \r
1313     PM_WN = (int) WhiteKnight, \r
1314     PM_WB = (int) WhiteBishop, \r
1315     PM_WR = (int) WhiteRook, \r
1316     PM_WQ = (int) WhiteQueen, \r
1317     PM_WF = (int) WhiteFerz, \r
1318     PM_WW = (int) WhiteWazir, \r
1319     PM_WE = (int) WhiteAlfil, \r
1320     PM_WM = (int) WhiteMan, \r
1321     PM_WO = (int) WhiteCannon, \r
1322     PM_WU = (int) WhiteUnicorn, \r
1323     PM_WH = (int) WhiteNightrider, \r
1324     PM_WA = (int) WhiteAngel, \r
1325     PM_WC = (int) WhiteMarshall, \r
1326     PM_WAB = (int) WhiteCardinal, \r
1327     PM_WD = (int) WhiteDragon, \r
1328     PM_WL = (int) WhiteLance, \r
1329     PM_WS = (int) WhiteCobra, \r
1330     PM_WV = (int) WhiteFalcon, \r
1331     PM_WSG = (int) WhiteSilver, \r
1332     PM_WG = (int) WhiteGrasshopper, \r
1333     PM_WK = (int) WhiteKing,\r
1334     PM_BP = (int) BlackPawn, \r
1335     PM_BN = (int) BlackKnight, \r
1336     PM_BB = (int) BlackBishop, \r
1337     PM_BR = (int) BlackRook, \r
1338     PM_BQ = (int) BlackQueen, \r
1339     PM_BF = (int) BlackFerz, \r
1340     PM_BW = (int) BlackWazir, \r
1341     PM_BE = (int) BlackAlfil, \r
1342     PM_BM = (int) BlackMan,\r
1343     PM_BO = (int) BlackCannon, \r
1344     PM_BU = (int) BlackUnicorn, \r
1345     PM_BH = (int) BlackNightrider, \r
1346     PM_BA = (int) BlackAngel, \r
1347     PM_BC = (int) BlackMarshall, \r
1348     PM_BG = (int) BlackGrasshopper, \r
1349     PM_BAB = (int) BlackCardinal,\r
1350     PM_BD = (int) BlackDragon,\r
1351     PM_BL = (int) BlackLance,\r
1352     PM_BS = (int) BlackCobra,\r
1353     PM_BV = (int) BlackFalcon,\r
1354     PM_BSG = (int) BlackSilver,\r
1355     PM_BK = (int) BlackKing\r
1356 };\r
1357 \r
1358 static HFONT hPieceFont = NULL;\r
1359 static HBITMAP hPieceMask[(int) EmptySquare];\r
1360 static HBITMAP hPieceFace[(int) EmptySquare];\r
1361 static int fontBitmapSquareSize = 0;\r
1362 static char pieceToFontChar[(int) EmptySquare] =\r
1363                               { 'p', 'n', 'b', 'r', 'q', \r
1364                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1365                       'k', 'o', 'm', 'v', 't', 'w', \r
1366                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1367                                                               'l' };\r
1368 \r
1369 extern BOOL SetCharTable( char *table, const char * map );\r
1370 /* [HGM] moved to backend.c */\r
1371 \r
1372 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1373 {\r
1374     HBRUSH hbrush;\r
1375     BYTE r1 = GetRValue( color );\r
1376     BYTE g1 = GetGValue( color );\r
1377     BYTE b1 = GetBValue( color );\r
1378     BYTE r2 = r1 / 2;\r
1379     BYTE g2 = g1 / 2;\r
1380     BYTE b2 = b1 / 2;\r
1381     RECT rc;\r
1382 \r
1383     /* Create a uniform background first */\r
1384     hbrush = CreateSolidBrush( color );\r
1385     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1386     FillRect( hdc, &rc, hbrush );\r
1387     DeleteObject( hbrush );\r
1388     \r
1389     if( mode == 1 ) {\r
1390         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1391         int steps = squareSize / 2;\r
1392         int i;\r
1393 \r
1394         for( i=0; i<steps; i++ ) {\r
1395             BYTE r = r1 - (r1-r2) * i / steps;\r
1396             BYTE g = g1 - (g1-g2) * i / steps;\r
1397             BYTE b = b1 - (b1-b2) * i / steps;\r
1398 \r
1399             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1400             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1401             FillRect( hdc, &rc, hbrush );\r
1402             DeleteObject(hbrush);\r
1403         }\r
1404     }\r
1405     else if( mode == 2 ) {\r
1406         /* Diagonal gradient, good more or less for every piece */\r
1407         POINT triangle[3];\r
1408         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1409         HBRUSH hbrush_old;\r
1410         int steps = squareSize;\r
1411         int i;\r
1412 \r
1413         triangle[0].x = squareSize - steps;\r
1414         triangle[0].y = squareSize;\r
1415         triangle[1].x = squareSize;\r
1416         triangle[1].y = squareSize;\r
1417         triangle[2].x = squareSize;\r
1418         triangle[2].y = squareSize - steps;\r
1419 \r
1420         for( i=0; i<steps; i++ ) {\r
1421             BYTE r = r1 - (r1-r2) * i / steps;\r
1422             BYTE g = g1 - (g1-g2) * i / steps;\r
1423             BYTE b = b1 - (b1-b2) * i / steps;\r
1424 \r
1425             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1426             hbrush_old = SelectObject( hdc, hbrush );\r
1427             Polygon( hdc, triangle, 3 );\r
1428             SelectObject( hdc, hbrush_old );\r
1429             DeleteObject(hbrush);\r
1430             triangle[0].x++;\r
1431             triangle[2].y++;\r
1432         }\r
1433 \r
1434         SelectObject( hdc, hpen );\r
1435     }\r
1436 }\r
1437 \r
1438 /*\r
1439     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1440     seems to work ok. The main problem here is to find the "inside" of a chess\r
1441     piece: follow the steps as explained below.\r
1442 */\r
1443 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1444 {\r
1445     HBITMAP hbm;\r
1446     HBITMAP hbm_old;\r
1447     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1448     RECT rc;\r
1449     SIZE sz;\r
1450     POINT pt;\r
1451     int backColor = whitePieceColor; \r
1452     int foreColor = blackPieceColor;\r
1453     \r
1454     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1455         backColor = appData.fontBackColorWhite;\r
1456         foreColor = appData.fontForeColorWhite;\r
1457     }\r
1458     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1459         backColor = appData.fontBackColorBlack;\r
1460         foreColor = appData.fontForeColorBlack;\r
1461     }\r
1462 \r
1463     /* Mask */\r
1464     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1465 \r
1466     hbm_old = SelectObject( hdc, hbm );\r
1467 \r
1468     rc.left = 0;\r
1469     rc.top = 0;\r
1470     rc.right = squareSize;\r
1471     rc.bottom = squareSize;\r
1472 \r
1473     /* Step 1: background is now black */\r
1474     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1475 \r
1476     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1477 \r
1478     pt.x = (squareSize - sz.cx) / 2;\r
1479     pt.y = (squareSize - sz.cy) / 2;\r
1480 \r
1481     SetBkMode( hdc, TRANSPARENT );\r
1482     SetTextColor( hdc, chroma );\r
1483     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1484     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1485 \r
1486     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1487     /* Step 3: the area outside the piece is filled with white */\r
1488 //    FloodFill( hdc, 0, 0, chroma );\r
1489     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1490     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1491     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1492     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1493     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1494     /* \r
1495         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1496         but if the start point is not inside the piece we're lost!\r
1497         There should be a better way to do this... if we could create a region or path\r
1498         from the fill operation we would be fine for example.\r
1499     */\r
1500 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1501     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1502 \r
1503     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1504         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1505         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1506 \r
1507         SelectObject( dc2, bm2 );\r
1508         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1509         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1510         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1511         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1512         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1513 \r
1514         DeleteDC( dc2 );\r
1515         DeleteObject( bm2 );\r
1516     }\r
1517 \r
1518     SetTextColor( hdc, 0 );\r
1519     /* \r
1520         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1521         draw the piece again in black for safety.\r
1522     */\r
1523     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1524 \r
1525     SelectObject( hdc, hbm_old );\r
1526 \r
1527     if( hPieceMask[index] != NULL ) {\r
1528         DeleteObject( hPieceMask[index] );\r
1529     }\r
1530 \r
1531     hPieceMask[index] = hbm;\r
1532 \r
1533     /* Face */\r
1534     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1535 \r
1536     SelectObject( hdc, hbm );\r
1537 \r
1538     {\r
1539         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1540         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1541         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1542 \r
1543         SelectObject( dc1, hPieceMask[index] );\r
1544         SelectObject( dc2, bm2 );\r
1545         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1546         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1547         \r
1548         /* \r
1549             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1550             the piece background and deletes (makes transparent) the rest.\r
1551             Thanks to that mask, we are free to paint the background with the greates\r
1552             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1553             We use this, to make gradients and give the pieces a "roundish" look.\r
1554         */\r
1555         SetPieceBackground( hdc, backColor, 2 );\r
1556         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1557 \r
1558         DeleteDC( dc2 );\r
1559         DeleteDC( dc1 );\r
1560         DeleteObject( bm2 );\r
1561     }\r
1562 \r
1563     SetTextColor( hdc, foreColor );\r
1564     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1565 \r
1566     SelectObject( hdc, hbm_old );\r
1567 \r
1568     if( hPieceFace[index] != NULL ) {\r
1569         DeleteObject( hPieceFace[index] );\r
1570     }\r
1571 \r
1572     hPieceFace[index] = hbm;\r
1573 }\r
1574 \r
1575 static int TranslatePieceToFontPiece( int piece )\r
1576 {\r
1577     switch( piece ) {\r
1578     case BlackPawn:\r
1579         return PM_BP;\r
1580     case BlackKnight:\r
1581         return PM_BN;\r
1582     case BlackBishop:\r
1583         return PM_BB;\r
1584     case BlackRook:\r
1585         return PM_BR;\r
1586     case BlackQueen:\r
1587         return PM_BQ;\r
1588     case BlackKing:\r
1589         return PM_BK;\r
1590     case WhitePawn:\r
1591         return PM_WP;\r
1592     case WhiteKnight:\r
1593         return PM_WN;\r
1594     case WhiteBishop:\r
1595         return PM_WB;\r
1596     case WhiteRook:\r
1597         return PM_WR;\r
1598     case WhiteQueen:\r
1599         return PM_WQ;\r
1600     case WhiteKing:\r
1601         return PM_WK;\r
1602 \r
1603     case BlackAngel:\r
1604         return PM_BA;\r
1605     case BlackMarshall:\r
1606         return PM_BC;\r
1607     case BlackFerz:\r
1608         return PM_BF;\r
1609     case BlackNightrider:\r
1610         return PM_BH;\r
1611     case BlackAlfil:\r
1612         return PM_BE;\r
1613     case BlackWazir:\r
1614         return PM_BW;\r
1615     case BlackUnicorn:\r
1616         return PM_BU;\r
1617     case BlackCannon:\r
1618         return PM_BO;\r
1619     case BlackGrasshopper:\r
1620         return PM_BG;\r
1621     case BlackMan:\r
1622         return PM_BM;\r
1623     case BlackSilver:\r
1624         return PM_BSG;\r
1625     case BlackLance:\r
1626         return PM_BL;\r
1627     case BlackFalcon:\r
1628         return PM_BV;\r
1629     case BlackCobra:\r
1630         return PM_BS;\r
1631     case BlackCardinal:\r
1632         return PM_BAB;\r
1633     case BlackDragon:\r
1634         return PM_BD;\r
1635 \r
1636     case WhiteAngel:\r
1637         return PM_WA;\r
1638     case WhiteMarshall:\r
1639         return PM_WC;\r
1640     case WhiteFerz:\r
1641         return PM_WF;\r
1642     case WhiteNightrider:\r
1643         return PM_WH;\r
1644     case WhiteAlfil:\r
1645         return PM_WE;\r
1646     case WhiteWazir:\r
1647         return PM_WW;\r
1648     case WhiteUnicorn:\r
1649         return PM_WU;\r
1650     case WhiteCannon:\r
1651         return PM_WO;\r
1652     case WhiteGrasshopper:\r
1653         return PM_WG;\r
1654     case WhiteMan:\r
1655         return PM_WM;\r
1656     case WhiteSilver:\r
1657         return PM_WSG;\r
1658     case WhiteLance:\r
1659         return PM_WL;\r
1660     case WhiteFalcon:\r
1661         return PM_WV;\r
1662     case WhiteCobra:\r
1663         return PM_WS;\r
1664     case WhiteCardinal:\r
1665         return PM_WAB;\r
1666     case WhiteDragon:\r
1667         return PM_WD;\r
1668     }\r
1669 \r
1670     return 0;\r
1671 }\r
1672 \r
1673 void CreatePiecesFromFont()\r
1674 {\r
1675     LOGFONT lf;\r
1676     HDC hdc_window = NULL;\r
1677     HDC hdc = NULL;\r
1678     HFONT hfont_old;\r
1679     int fontHeight;\r
1680     int i;\r
1681 \r
1682     if( fontBitmapSquareSize < 0 ) {\r
1683         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1684         return;\r
1685     }\r
1686 \r
1687     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1688         fontBitmapSquareSize = -1;\r
1689         return;\r
1690     }\r
1691 \r
1692     if( fontBitmapSquareSize != squareSize ) {\r
1693         hdc_window = GetDC( hwndMain );\r
1694         hdc = CreateCompatibleDC( hdc_window );\r
1695 \r
1696         if( hPieceFont != NULL ) {\r
1697             DeleteObject( hPieceFont );\r
1698         }\r
1699         else {\r
1700             for( i=0; i<=(int)BlackKing; i++ ) {\r
1701                 hPieceMask[i] = NULL;\r
1702                 hPieceFace[i] = NULL;\r
1703             }\r
1704         }\r
1705 \r
1706         fontHeight = 75;\r
1707 \r
1708         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1709             fontHeight = appData.fontPieceSize;\r
1710         }\r
1711 \r
1712         fontHeight = (fontHeight * squareSize) / 100;\r
1713 \r
1714         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1715         lf.lfWidth = 0;\r
1716         lf.lfEscapement = 0;\r
1717         lf.lfOrientation = 0;\r
1718         lf.lfWeight = FW_NORMAL;\r
1719         lf.lfItalic = 0;\r
1720         lf.lfUnderline = 0;\r
1721         lf.lfStrikeOut = 0;\r
1722         lf.lfCharSet = DEFAULT_CHARSET;\r
1723         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1724         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1725         lf.lfQuality = PROOF_QUALITY;\r
1726         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
1727         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
1728         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
1729 \r
1730         hPieceFont = CreateFontIndirect( &lf );\r
1731 \r
1732         if( hPieceFont == NULL ) {\r
1733             fontBitmapSquareSize = -2;\r
1734         }\r
1735         else {\r
1736             /* Setup font-to-piece character table */\r
1737             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
1738                 /* No (or wrong) global settings, try to detect the font */\r
1739                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
1740                     /* Alpha */\r
1741                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
1742                 }\r
1743                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
1744                     /* DiagramTT* family */\r
1745                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
1746                 }\r
1747                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
1748                     /* Fairy symbols */\r
1749                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
1750                 }\r
1751                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
1752                     /* Good Companion (Some characters get warped as literal :-( */\r
1753                     char s[] = "1cmWG0??S??oYI23wgQU";\r
1754                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
1755                     SetCharTable(pieceToFontChar, s);\r
1756                 }\r
1757                 else {\r
1758                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
1759                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
1760                 }\r
1761             }\r
1762 \r
1763             /* Create bitmaps */\r
1764             hfont_old = SelectObject( hdc, hPieceFont );\r
1765             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
1766                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
1767                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
1768 \r
1769             SelectObject( hdc, hfont_old );\r
1770 \r
1771             fontBitmapSquareSize = squareSize;\r
1772         }\r
1773     }\r
1774 \r
1775     if( hdc != NULL ) {\r
1776         DeleteDC( hdc );\r
1777     }\r
1778 \r
1779     if( hdc_window != NULL ) {\r
1780         ReleaseDC( hwndMain, hdc_window );\r
1781     }\r
1782 }\r
1783 \r
1784 HBITMAP\r
1785 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1786 {\r
1787   char name[128];\r
1788 \r
1789   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1790   if (gameInfo.event &&\r
1791       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1792       strcmp(name, "k80s") == 0) {\r
1793     strcpy(name, "tim");\r
1794   }\r
1795   return LoadBitmap(hinst, name);\r
1796 }\r
1797 \r
1798 \r
1799 /* Insert a color into the program's logical palette\r
1800    structure.  This code assumes the given color is\r
1801    the result of the RGB or PALETTERGB macro, and it\r
1802    knows how those macros work (which is documented).\r
1803 */\r
1804 VOID\r
1805 InsertInPalette(COLORREF color)\r
1806 {\r
1807   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1808 \r
1809   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1810     DisplayFatalError("Too many colors", 0, 1);\r
1811     pLogPal->palNumEntries--;\r
1812     return;\r
1813   }\r
1814 \r
1815   pe->peFlags = (char) 0;\r
1816   pe->peRed = (char) (0xFF & color);\r
1817   pe->peGreen = (char) (0xFF & (color >> 8));\r
1818   pe->peBlue = (char) (0xFF & (color >> 16));\r
1819   return;\r
1820 }\r
1821 \r
1822 \r
1823 VOID\r
1824 InitDrawingColors()\r
1825 {\r
1826   if (pLogPal == NULL) {\r
1827     /* Allocate enough memory for a logical palette with\r
1828      * PALETTESIZE entries and set the size and version fields\r
1829      * of the logical palette structure.\r
1830      */\r
1831     pLogPal = (NPLOGPALETTE)\r
1832       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
1833                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
1834     pLogPal->palVersion    = 0x300;\r
1835   }\r
1836   pLogPal->palNumEntries = 0;\r
1837 \r
1838   InsertInPalette(lightSquareColor);\r
1839   InsertInPalette(darkSquareColor);\r
1840   InsertInPalette(whitePieceColor);\r
1841   InsertInPalette(blackPieceColor);\r
1842   InsertInPalette(highlightSquareColor);\r
1843   InsertInPalette(premoveHighlightColor);\r
1844 \r
1845   /*  create a logical color palette according the information\r
1846    *  in the LOGPALETTE structure.\r
1847    */\r
1848   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
1849 \r
1850   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
1851   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
1852   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
1853   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
1854   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
1855   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
1856   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
1857   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
1858   /* [AS] Force rendering of the font-based pieces */\r
1859   if( fontBitmapSquareSize > 0 ) {\r
1860     fontBitmapSquareSize = 0;\r
1861   }\r
1862 }\r
1863 \r
1864 \r
1865 int\r
1866 BoardWidth(int boardSize, int n)\r
1867 { /* [HGM] argument n added to allow different width and height */\r
1868   int lineGap = sizeInfo[boardSize].lineGap;\r
1869 \r
1870   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1871       lineGap = appData.overrideLineGap;\r
1872   }\r
1873 \r
1874   return (n + 1) * lineGap +\r
1875           n * sizeInfo[boardSize].squareSize;\r
1876 }\r
1877 \r
1878 /* Respond to board resize by dragging edge */\r
1879 VOID\r
1880 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
1881 {\r
1882   BoardSize newSize = NUM_SIZES - 1;\r
1883   static int recurse = 0;\r
1884   if (IsIconic(hwndMain)) return;\r
1885   if (recurse > 0) return;\r
1886   recurse++;\r
1887   while (newSize > 0) {\r
1888         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
1889         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
1890            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
1891     newSize--;\r
1892   } \r
1893   boardSize = newSize;\r
1894   InitDrawingSizes(boardSize, flags);\r
1895   recurse--;\r
1896 }\r
1897 \r
1898 \r
1899 \r
1900 VOID\r
1901 InitDrawingSizes(BoardSize boardSize, int flags)\r
1902 {\r
1903   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
1904   ChessSquare piece;\r
1905   static int oldBoardSize = -1, oldTinyLayout = 0;\r
1906   HDC hdc;\r
1907   SIZE clockSize, messageSize;\r
1908   HFONT oldFont;\r
1909   char buf[MSG_SIZ];\r
1910   char *str;\r
1911   HMENU hmenu = GetMenu(hwndMain);\r
1912   RECT crect, wrect, oldRect;\r
1913   int offby;\r
1914   LOGBRUSH logbrush;\r
1915 \r
1916   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
1917   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
1918 \r
1919   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
1920   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
1921 \r
1922   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
1923   oldRect.top = wpMain.y;\r
1924   oldRect.right = wpMain.x + wpMain.width;\r
1925   oldRect.bottom = wpMain.y + wpMain.height;\r
1926 \r
1927   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
1928   smallLayout = sizeInfo[boardSize].smallLayout;\r
1929   squareSize = sizeInfo[boardSize].squareSize;\r
1930   lineGap = sizeInfo[boardSize].lineGap;\r
1931   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
1932 \r
1933   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1934       lineGap = appData.overrideLineGap;\r
1935   }\r
1936 \r
1937   if (tinyLayout != oldTinyLayout) {\r
1938     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
1939     if (tinyLayout) {\r
1940       style &= ~WS_SYSMENU;\r
1941       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
1942                  "&Minimize\tCtrl+F4");\r
1943     } else {\r
1944       style |= WS_SYSMENU;\r
1945       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
1946     }\r
1947     SetWindowLong(hwndMain, GWL_STYLE, style);\r
1948 \r
1949     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
1950       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
1951         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
1952     }\r
1953     DrawMenuBar(hwndMain);\r
1954   }\r
1955 \r
1956   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
1957   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
1958 \r
1959   /* Get text area sizes */\r
1960   hdc = GetDC(hwndMain);\r
1961   if (appData.clockMode) {\r
1962     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
1963   } else {\r
1964     sprintf(buf, "White");\r
1965   }\r
1966   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
1967   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
1968   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
1969   str = "We only care about the height here";\r
1970   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
1971   SelectObject(hdc, oldFont);\r
1972   ReleaseDC(hwndMain, hdc);\r
1973 \r
1974   /* Compute where everything goes */\r
1975   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
1976         /* [HGM] logo: if either logo is on, reserve space for it */\r
1977         logoHeight =  2*clockSize.cy;\r
1978         leftLogoRect.left   = OUTER_MARGIN;\r
1979         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
1980         leftLogoRect.top    = OUTER_MARGIN;\r
1981         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1982 \r
1983         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
1984         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
1985         rightLogoRect.top    = OUTER_MARGIN;\r
1986         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1987 \r
1988 \r
1989     whiteRect.left = leftLogoRect.right;\r
1990     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
1991     whiteRect.top = OUTER_MARGIN;\r
1992     whiteRect.bottom = whiteRect.top + logoHeight;\r
1993 \r
1994     blackRect.right = rightLogoRect.left;\r
1995     blackRect.left = whiteRect.right + INNER_MARGIN;\r
1996     blackRect.top = whiteRect.top;\r
1997     blackRect.bottom = whiteRect.bottom;\r
1998   } else {\r
1999     whiteRect.left = OUTER_MARGIN;\r
2000     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2001     whiteRect.top = OUTER_MARGIN;\r
2002     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2003 \r
2004     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2005     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2006     blackRect.top = whiteRect.top;\r
2007     blackRect.bottom = whiteRect.bottom;\r
2008 \r
2009     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2010   }\r
2011 \r
2012   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2013   if (appData.showButtonBar) {\r
2014     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2015       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2016   } else {\r
2017     messageRect.right = OUTER_MARGIN + boardWidth;\r
2018   }\r
2019   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2020   messageRect.bottom = messageRect.top + messageSize.cy;\r
2021 \r
2022   boardRect.left = OUTER_MARGIN;\r
2023   boardRect.right = boardRect.left + boardWidth;\r
2024   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2025   boardRect.bottom = boardRect.top + boardHeight;\r
2026 \r
2027   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2028   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2029   oldBoardSize = boardSize;\r
2030   oldTinyLayout = tinyLayout;\r
2031   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2032   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2033     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2034   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2035   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2036   wpMain.height = winH; //       without disturbing window attachments\r
2037   GetWindowRect(hwndMain, &wrect);\r
2038   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2039                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2040 \r
2041   // [HGM] placement: let attached windows follow size change.\r
2042   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2043   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2044   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2045   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2046   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2047 \r
2048   /* compensate if menu bar wrapped */\r
2049   GetClientRect(hwndMain, &crect);\r
2050   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2051   wpMain.height += offby;\r
2052   switch (flags) {\r
2053   case WMSZ_TOPLEFT:\r
2054     SetWindowPos(hwndMain, NULL, \r
2055                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2056                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2057     break;\r
2058 \r
2059   case WMSZ_TOPRIGHT:\r
2060   case WMSZ_TOP:\r
2061     SetWindowPos(hwndMain, NULL, \r
2062                  wrect.left, wrect.bottom - wpMain.height, \r
2063                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2064     break;\r
2065 \r
2066   case WMSZ_BOTTOMLEFT:\r
2067   case WMSZ_LEFT:\r
2068     SetWindowPos(hwndMain, NULL, \r
2069                  wrect.right - wpMain.width, wrect.top, \r
2070                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2071     break;\r
2072 \r
2073   case WMSZ_BOTTOMRIGHT:\r
2074   case WMSZ_BOTTOM:\r
2075   case WMSZ_RIGHT:\r
2076   default:\r
2077     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2078                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2079     break;\r
2080   }\r
2081 \r
2082   hwndPause = NULL;\r
2083   for (i = 0; i < N_BUTTONS; i++) {\r
2084     if (buttonDesc[i].hwnd != NULL) {\r
2085       DestroyWindow(buttonDesc[i].hwnd);\r
2086       buttonDesc[i].hwnd = NULL;\r
2087     }\r
2088     if (appData.showButtonBar) {\r
2089       buttonDesc[i].hwnd =\r
2090         CreateWindow("BUTTON", buttonDesc[i].label,\r
2091                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2092                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2093                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2094                      (HMENU) buttonDesc[i].id,\r
2095                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2096       if (tinyLayout) {\r
2097         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2098                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2099                     MAKELPARAM(FALSE, 0));\r
2100       }\r
2101       if (buttonDesc[i].id == IDM_Pause)\r
2102         hwndPause = buttonDesc[i].hwnd;\r
2103       buttonDesc[i].wndproc = (WNDPROC)\r
2104         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2105     }\r
2106   }\r
2107   if (gridPen != NULL) DeleteObject(gridPen);\r
2108   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2109   if (premovePen != NULL) DeleteObject(premovePen);\r
2110   if (lineGap != 0) {\r
2111     logbrush.lbStyle = BS_SOLID;\r
2112     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2113     gridPen =\r
2114       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2115                    lineGap, &logbrush, 0, NULL);\r
2116     logbrush.lbColor = highlightSquareColor;\r
2117     highlightPen =\r
2118       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2119                    lineGap, &logbrush, 0, NULL);\r
2120 \r
2121     logbrush.lbColor = premoveHighlightColor; \r
2122     premovePen =\r
2123       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2124                    lineGap, &logbrush, 0, NULL);\r
2125 \r
2126     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2127     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2128       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2129       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2130         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2131       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2132         BOARD_WIDTH * (squareSize + lineGap);\r
2133       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2134     }\r
2135     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2136       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2137       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2138         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2139         lineGap / 2 + (i * (squareSize + lineGap));\r
2140       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2141         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2142       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2143     }\r
2144   }\r
2145 \r
2146   /* [HGM] Licensing requirement */\r
2147 #ifdef GOTHIC\r
2148   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2149 #endif\r
2150 #ifdef FALCON\r
2151   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2152 #endif\r
2153   GothicPopUp( "", VariantNormal);\r
2154 \r
2155 \r
2156 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2157 \r
2158   /* Load piece bitmaps for this board size */\r
2159   for (i=0; i<=2; i++) {\r
2160     for (piece = WhitePawn;\r
2161          (int) piece < (int) BlackPawn;\r
2162          piece = (ChessSquare) ((int) piece + 1)) {\r
2163       if (pieceBitmap[i][piece] != NULL)\r
2164         DeleteObject(pieceBitmap[i][piece]);\r
2165     }\r
2166   }\r
2167 \r
2168   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2169   // Orthodox Chess pieces\r
2170   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2171   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2172   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2173   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2174   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2175   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2176   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2177   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2178   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2179   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2180   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2181   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2182   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2183   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2184   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2185   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2186     // in Shogi, Hijack the unused Queen for Lance\r
2187     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2188     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2189     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2190   } else {\r
2191     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2192     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2193     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2194   }\r
2195 \r
2196   if(squareSize <= 72 && squareSize >= 33) { \r
2197     /* A & C are available in most sizes now */\r
2198     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2199       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2200       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2201       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2202       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2203       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2204       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2205       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2206       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2207       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2208       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2209       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2210       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2211     } else { // Smirf-like\r
2212       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2213       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2214       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2215     }\r
2216     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2217       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2218       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2219       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2220     } else { // WinBoard standard\r
2221       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2222       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2223       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2224     }\r
2225   }\r
2226 \r
2227 \r
2228   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2229     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2230     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2231     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2232     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2233     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2234     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2235     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2236     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2237     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2238     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2239     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2240     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2241     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2242     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2243     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2244     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2245     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2246     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2247     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2248     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2249     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2250     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2251     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2252     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2253     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2254     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2255     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2256     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2257     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2258     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2259 \r
2260     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2261       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2262       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2263       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2264       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2265       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2266       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2267       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2268       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2269       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2270       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2271       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2272       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2273     } else {\r
2274       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2275       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2276       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2277       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2278       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2279       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2280       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2281       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2282       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2283       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2284       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2285       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2286     }\r
2287 \r
2288   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2289     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2290     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2291     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2292     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2293     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2294     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2295     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2296     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2297     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2298     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2299     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2300     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2301     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2302     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2303   }\r
2304 \r
2305 \r
2306   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2307   /* special Shogi support in this size */\r
2308   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2309       for (piece = WhitePawn;\r
2310            (int) piece < (int) BlackPawn;\r
2311            piece = (ChessSquare) ((int) piece + 1)) {\r
2312         if (pieceBitmap[i][piece] != NULL)\r
2313           DeleteObject(pieceBitmap[i][piece]);\r
2314       }\r
2315     }\r
2316   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2317   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2318   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2319   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2320   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2321   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2322   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2323   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2324   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2325   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2326   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2327   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2328   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2329   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2330   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2331   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2332   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2333   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2334   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2335   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2336   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2337   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2338   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2339   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2340   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2341   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2342   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2343   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2344   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2345   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2346   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2347   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2348   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2349   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2350   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2351   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2352   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2353   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2354   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2355   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2356   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2357   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2358   minorSize = 0;\r
2359   }\r
2360 }\r
2361 \r
2362 HBITMAP\r
2363 PieceBitmap(ChessSquare p, int kind)\r
2364 {\r
2365   if ((int) p >= (int) BlackPawn)\r
2366     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2367 \r
2368   return pieceBitmap[kind][(int) p];\r
2369 }\r
2370 \r
2371 /***************************************************************/\r
2372 \r
2373 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2374 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2375 /*\r
2376 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2377 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2378 */\r
2379 \r
2380 VOID\r
2381 SquareToPos(int row, int column, int * x, int * y)\r
2382 {\r
2383   if (flipView) {\r
2384     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2385     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2386   } else {\r
2387     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2388     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2389   }\r
2390 }\r
2391 \r
2392 VOID\r
2393 DrawCoordsOnDC(HDC hdc)\r
2394 {\r
2395   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
2396   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
2397   char str[2] = { NULLCHAR, NULLCHAR };\r
2398   int oldMode, oldAlign, x, y, start, i;\r
2399   HFONT oldFont;\r
2400   HBRUSH oldBrush;\r
2401 \r
2402   if (!appData.showCoords)\r
2403     return;\r
2404 \r
2405   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2406 \r
2407   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2408   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2409   oldAlign = GetTextAlign(hdc);\r
2410   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2411 \r
2412   y = boardRect.top + lineGap;\r
2413   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2414 \r
2415   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2416   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2417     str[0] = files[start + i];\r
2418     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2419     y += squareSize + lineGap;\r
2420   }\r
2421 \r
2422   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2423 \r
2424   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2425   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2426     str[0] = ranks[start + i];\r
2427     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2428     x += squareSize + lineGap;\r
2429   }    \r
2430 \r
2431   SelectObject(hdc, oldBrush);\r
2432   SetBkMode(hdc, oldMode);\r
2433   SetTextAlign(hdc, oldAlign);\r
2434   SelectObject(hdc, oldFont);\r
2435 }\r
2436 \r
2437 VOID\r
2438 DrawGridOnDC(HDC hdc)\r
2439 {\r
2440   HPEN oldPen;\r
2441  \r
2442   if (lineGap != 0) {\r
2443     oldPen = SelectObject(hdc, gridPen);\r
2444     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2445     SelectObject(hdc, oldPen);\r
2446   }\r
2447 }\r
2448 \r
2449 #define HIGHLIGHT_PEN 0\r
2450 #define PREMOVE_PEN   1\r
2451 \r
2452 VOID\r
2453 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2454 {\r
2455   int x1, y1;\r
2456   HPEN oldPen, hPen;\r
2457   if (lineGap == 0) return;\r
2458   if (flipView) {\r
2459     x1 = boardRect.left +\r
2460       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2461     y1 = boardRect.top +\r
2462       lineGap/2 + y * (squareSize + lineGap);\r
2463   } else {\r
2464     x1 = boardRect.left +\r
2465       lineGap/2 + x * (squareSize + lineGap);\r
2466     y1 = boardRect.top +\r
2467       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2468   }\r
2469   hPen = pen ? premovePen : highlightPen;\r
2470   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2471   MoveToEx(hdc, x1, y1, NULL);\r
2472   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2473   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2474   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2475   LineTo(hdc, x1, y1);\r
2476   SelectObject(hdc, oldPen);\r
2477 }\r
2478 \r
2479 VOID\r
2480 DrawHighlightsOnDC(HDC hdc)\r
2481 {\r
2482   int i;\r
2483   for (i=0; i<2; i++) {\r
2484     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2485       DrawHighlightOnDC(hdc, TRUE,\r
2486                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2487                         HIGHLIGHT_PEN);\r
2488   }\r
2489   for (i=0; i<2; i++) {\r
2490     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2491         premoveHighlightInfo.sq[i].y >= 0) {\r
2492         DrawHighlightOnDC(hdc, TRUE,\r
2493                           premoveHighlightInfo.sq[i].x, \r
2494                           premoveHighlightInfo.sq[i].y,\r
2495                           PREMOVE_PEN);\r
2496     }\r
2497   }\r
2498 }\r
2499 \r
2500 /* Note: sqcolor is used only in monoMode */\r
2501 /* Note that this code is largely duplicated in woptions.c,\r
2502    function DrawSampleSquare, so that needs to be updated too */\r
2503 VOID\r
2504 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2505 {\r
2506   HBITMAP oldBitmap;\r
2507   HBRUSH oldBrush;\r
2508   int tmpSize;\r
2509 \r
2510   if (appData.blindfold) return;\r
2511 \r
2512   /* [AS] Use font-based pieces if needed */\r
2513   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
2514     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2515     CreatePiecesFromFont();\r
2516 \r
2517     if( fontBitmapSquareSize == squareSize ) {\r
2518         int index = TranslatePieceToFontPiece(piece);\r
2519 \r
2520         SelectObject( tmphdc, hPieceMask[ index ] );\r
2521 \r
2522         BitBlt( hdc,\r
2523             x, y,\r
2524             squareSize, squareSize,\r
2525             tmphdc,\r
2526             0, 0,\r
2527             SRCAND );\r
2528 \r
2529         SelectObject( tmphdc, hPieceFace[ index ] );\r
2530 \r
2531         BitBlt( hdc,\r
2532             x, y,\r
2533             squareSize, squareSize,\r
2534             tmphdc,\r
2535             0, 0,\r
2536             SRCPAINT );\r
2537 \r
2538         return;\r
2539     }\r
2540   }\r
2541 \r
2542   if (appData.monoMode) {\r
2543     SelectObject(tmphdc, PieceBitmap(piece, \r
2544       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2545     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2546            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2547   } else {\r
2548     tmpSize = squareSize;\r
2549     if(minorSize &&\r
2550         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2551          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2552       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2553       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2554       x += (squareSize - minorSize)>>1;\r
2555       y += squareSize - minorSize - 2;\r
2556       tmpSize = minorSize;\r
2557     }\r
2558     if (color || appData.allWhite ) {\r
2559       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2560       if( color )\r
2561               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2562       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2563       if(appData.upsideDown && color==flipView)\r
2564         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2565       else\r
2566         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2567       /* Use black for outline of white pieces */\r
2568       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2569       if(appData.upsideDown && color==flipView)\r
2570         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2571       else\r
2572         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2573     } else {\r
2574       /* Use square color for details of black pieces */\r
2575       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2576       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2577       if(appData.upsideDown && !flipView)\r
2578         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2579       else\r
2580         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2581     }\r
2582     SelectObject(hdc, oldBrush);\r
2583     SelectObject(tmphdc, oldBitmap);\r
2584   }\r
2585 }\r
2586 \r
2587 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2588 int GetBackTextureMode( int algo )\r
2589 {\r
2590     int result = BACK_TEXTURE_MODE_DISABLED;\r
2591 \r
2592     switch( algo ) \r
2593     {\r
2594         case BACK_TEXTURE_MODE_PLAIN:\r
2595             result = 1; /* Always use identity map */\r
2596             break;\r
2597         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2598             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2599             break;\r
2600     }\r
2601 \r
2602     return result;\r
2603 }\r
2604 \r
2605 /* \r
2606     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2607     to handle redraws cleanly (as random numbers would always be different).\r
2608 */\r
2609 VOID RebuildTextureSquareInfo()\r
2610 {\r
2611     BITMAP bi;\r
2612     int lite_w = 0;\r
2613     int lite_h = 0;\r
2614     int dark_w = 0;\r
2615     int dark_h = 0;\r
2616     int row;\r
2617     int col;\r
2618 \r
2619     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2620 \r
2621     if( liteBackTexture != NULL ) {\r
2622         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2623             lite_w = bi.bmWidth;\r
2624             lite_h = bi.bmHeight;\r
2625         }\r
2626     }\r
2627 \r
2628     if( darkBackTexture != NULL ) {\r
2629         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2630             dark_w = bi.bmWidth;\r
2631             dark_h = bi.bmHeight;\r
2632         }\r
2633     }\r
2634 \r
2635     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2636         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2637             if( (col + row) & 1 ) {\r
2638                 /* Lite square */\r
2639                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2640                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2641                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2642                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2643                 }\r
2644             }\r
2645             else {\r
2646                 /* Dark square */\r
2647                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2648                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2649                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2650                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2651                 }\r
2652             }\r
2653         }\r
2654     }\r
2655 }\r
2656 \r
2657 /* [AS] Arrow highlighting support */\r
2658 \r
2659 static int A_WIDTH = 5; /* Width of arrow body */\r
2660 \r
2661 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2662 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2663 \r
2664 static double Sqr( double x )\r
2665 {\r
2666     return x*x;\r
2667 }\r
2668 \r
2669 static int Round( double x )\r
2670 {\r
2671     return (int) (x + 0.5);\r
2672 }\r
2673 \r
2674 /* Draw an arrow between two points using current settings */\r
2675 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2676 {\r
2677     POINT arrow[7];\r
2678     double dx, dy, j, k, x, y;\r
2679 \r
2680     if( d_x == s_x ) {\r
2681         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2682 \r
2683         arrow[0].x = s_x + A_WIDTH;\r
2684         arrow[0].y = s_y;\r
2685 \r
2686         arrow[1].x = s_x + A_WIDTH;\r
2687         arrow[1].y = d_y - h;\r
2688 \r
2689         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2690         arrow[2].y = d_y - h;\r
2691 \r
2692         arrow[3].x = d_x;\r
2693         arrow[3].y = d_y;\r
2694 \r
2695         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2696         arrow[4].y = d_y - h;\r
2697 \r
2698         arrow[5].x = s_x - A_WIDTH;\r
2699         arrow[5].y = d_y - h;\r
2700 \r
2701         arrow[6].x = s_x - A_WIDTH;\r
2702         arrow[6].y = s_y;\r
2703     }\r
2704     else if( d_y == s_y ) {\r
2705         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2706 \r
2707         arrow[0].x = s_x;\r
2708         arrow[0].y = s_y + A_WIDTH;\r
2709 \r
2710         arrow[1].x = d_x - w;\r
2711         arrow[1].y = s_y + A_WIDTH;\r
2712 \r
2713         arrow[2].x = d_x - w;\r
2714         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2715 \r
2716         arrow[3].x = d_x;\r
2717         arrow[3].y = d_y;\r
2718 \r
2719         arrow[4].x = d_x - w;\r
2720         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2721 \r
2722         arrow[5].x = d_x - w;\r
2723         arrow[5].y = s_y - A_WIDTH;\r
2724 \r
2725         arrow[6].x = s_x;\r
2726         arrow[6].y = s_y - A_WIDTH;\r
2727     }\r
2728     else {\r
2729         /* [AS] Needed a lot of paper for this! :-) */\r
2730         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2731         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2732   \r
2733         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2734 \r
2735         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2736 \r
2737         x = s_x;\r
2738         y = s_y;\r
2739 \r
2740         arrow[0].x = Round(x - j);\r
2741         arrow[0].y = Round(y + j*dx);\r
2742 \r
2743         arrow[1].x = Round(x + j);\r
2744         arrow[1].y = Round(y - j*dx);\r
2745 \r
2746         if( d_x > s_x ) {\r
2747             x = (double) d_x - k;\r
2748             y = (double) d_y - k*dy;\r
2749         }\r
2750         else {\r
2751             x = (double) d_x + k;\r
2752             y = (double) d_y + k*dy;\r
2753         }\r
2754 \r
2755         arrow[2].x = Round(x + j);\r
2756         arrow[2].y = Round(y - j*dx);\r
2757 \r
2758         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2759         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2760 \r
2761         arrow[4].x = d_x;\r
2762         arrow[4].y = d_y;\r
2763 \r
2764         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2765         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2766 \r
2767         arrow[6].x = Round(x - j);\r
2768         arrow[6].y = Round(y + j*dx);\r
2769     }\r
2770 \r
2771     Polygon( hdc, arrow, 7 );\r
2772 }\r
2773 \r
2774 /* [AS] Draw an arrow between two squares */\r
2775 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2776 {\r
2777     int s_x, s_y, d_x, d_y;\r
2778     HPEN hpen;\r
2779     HPEN holdpen;\r
2780     HBRUSH hbrush;\r
2781     HBRUSH holdbrush;\r
2782     LOGBRUSH stLB;\r
2783 \r
2784     if( s_col == d_col && s_row == d_row ) {\r
2785         return;\r
2786     }\r
2787 \r
2788     /* Get source and destination points */\r
2789     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2790     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2791 \r
2792     if( d_y > s_y ) {\r
2793         d_y += squareSize / 4;\r
2794     }\r
2795     else if( d_y < s_y ) {\r
2796         d_y += 3 * squareSize / 4;\r
2797     }\r
2798     else {\r
2799         d_y += squareSize / 2;\r
2800     }\r
2801 \r
2802     if( d_x > s_x ) {\r
2803         d_x += squareSize / 4;\r
2804     }\r
2805     else if( d_x < s_x ) {\r
2806         d_x += 3 * squareSize / 4;\r
2807     }\r
2808     else {\r
2809         d_x += squareSize / 2;\r
2810     }\r
2811 \r
2812     s_x += squareSize / 2;\r
2813     s_y += squareSize / 2;\r
2814 \r
2815     /* Adjust width */\r
2816     A_WIDTH = squareSize / 14;\r
2817 \r
2818     /* Draw */\r
2819     stLB.lbStyle = BS_SOLID;\r
2820     stLB.lbColor = appData.highlightArrowColor;\r
2821     stLB.lbHatch = 0;\r
2822 \r
2823     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2824     holdpen = SelectObject( hdc, hpen );\r
2825     hbrush = CreateBrushIndirect( &stLB );\r
2826     holdbrush = SelectObject( hdc, hbrush );\r
2827 \r
2828     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2829 \r
2830     SelectObject( hdc, holdpen );\r
2831     SelectObject( hdc, holdbrush );\r
2832     DeleteObject( hpen );\r
2833     DeleteObject( hbrush );\r
2834 }\r
2835 \r
2836 BOOL HasHighlightInfo()\r
2837 {\r
2838     BOOL result = FALSE;\r
2839 \r
2840     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2841         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2842     {\r
2843         result = TRUE;\r
2844     }\r
2845 \r
2846     return result;\r
2847 }\r
2848 \r
2849 BOOL IsDrawArrowEnabled()\r
2850 {\r
2851     BOOL result = FALSE;\r
2852 \r
2853     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2854         result = TRUE;\r
2855     }\r
2856 \r
2857     return result;\r
2858 }\r
2859 \r
2860 VOID DrawArrowHighlight( HDC hdc )\r
2861 {\r
2862     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2863         DrawArrowBetweenSquares( hdc,\r
2864             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2865             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2866     }\r
2867 }\r
2868 \r
2869 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2870 {\r
2871     HRGN result = NULL;\r
2872 \r
2873     if( HasHighlightInfo() ) {\r
2874         int x1, y1, x2, y2;\r
2875         int sx, sy, dx, dy;\r
2876 \r
2877         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2878         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2879 \r
2880         sx = MIN( x1, x2 );\r
2881         sy = MIN( y1, y2 );\r
2882         dx = MAX( x1, x2 ) + squareSize;\r
2883         dy = MAX( y1, y2 ) + squareSize;\r
2884 \r
2885         result = CreateRectRgn( sx, sy, dx, dy );\r
2886     }\r
2887 \r
2888     return result;\r
2889 }\r
2890 \r
2891 /*\r
2892     Warning: this function modifies the behavior of several other functions. \r
2893     \r
2894     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2895     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2896     repaint is scattered all over the place, which is not good for features such as\r
2897     "arrow highlighting" that require a full repaint of the board.\r
2898 \r
2899     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2900     user interaction, when speed is not so important) but especially to avoid errors\r
2901     in the displayed graphics.\r
2902 \r
2903     In such patched places, I always try refer to this function so there is a single\r
2904     place to maintain knowledge.\r
2905     \r
2906     To restore the original behavior, just return FALSE unconditionally.\r
2907 */\r
2908 BOOL IsFullRepaintPreferrable()\r
2909 {\r
2910     BOOL result = FALSE;\r
2911 \r
2912     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2913         /* Arrow may appear on the board */\r
2914         result = TRUE;\r
2915     }\r
2916 \r
2917     return result;\r
2918 }\r
2919 \r
2920 /* \r
2921     This function is called by DrawPosition to know whether a full repaint must\r
2922     be forced or not.\r
2923 \r
2924     Only DrawPosition may directly call this function, which makes use of \r
2925     some state information. Other function should call DrawPosition specifying \r
2926     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2927 */\r
2928 BOOL DrawPositionNeedsFullRepaint()\r
2929 {\r
2930     BOOL result = FALSE;\r
2931 \r
2932     /* \r
2933         Probably a slightly better policy would be to trigger a full repaint\r
2934         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2935         but animation is fast enough that it's difficult to notice.\r
2936     */\r
2937     if( animInfo.piece == EmptySquare ) {\r
2938         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2939             result = TRUE;\r
2940         }\r
2941     }\r
2942 \r
2943     return result;\r
2944 }\r
2945 \r
2946 VOID\r
2947 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2948 {\r
2949   int row, column, x, y, square_color, piece_color;\r
2950   ChessSquare piece;\r
2951   HBRUSH oldBrush;\r
2952   HDC texture_hdc = NULL;\r
2953 \r
2954   /* [AS] Initialize background textures if needed */\r
2955   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2956       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2957       if( backTextureSquareSize != squareSize \r
2958        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2959           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2960           backTextureSquareSize = squareSize;\r
2961           RebuildTextureSquareInfo();\r
2962       }\r
2963 \r
2964       texture_hdc = CreateCompatibleDC( hdc );\r
2965   }\r
2966 \r
2967   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2968     for (column = 0; column < BOARD_WIDTH; column++) {\r
2969   \r
2970       SquareToPos(row, column, &x, &y);\r
2971 \r
2972       piece = board[row][column];\r
2973 \r
2974       square_color = ((column + row) % 2) == 1;\r
2975       if( gameInfo.variant == VariantXiangqi ) {\r
2976           square_color = !InPalace(row, column);\r
2977           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
2978           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
2979       }\r
2980       piece_color = (int) piece < (int) BlackPawn;\r
2981 \r
2982 \r
2983       /* [HGM] holdings file: light square or black */\r
2984       if(column == BOARD_LEFT-2) {\r
2985             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
2986                 square_color = 1;\r
2987             else {\r
2988                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
2989                 continue;\r
2990             }\r
2991       } else\r
2992       if(column == BOARD_RGHT + 1 ) {\r
2993             if( row < gameInfo.holdingsSize )\r
2994                 square_color = 1;\r
2995             else {\r
2996                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
2997                 continue;\r
2998             }\r
2999       }\r
3000       if(column == BOARD_LEFT-1 ) /* left align */\r
3001             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3002       else if( column == BOARD_RGHT) /* right align */\r
3003             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3004       else\r
3005       if (appData.monoMode) {\r
3006         if (piece == EmptySquare) {\r
3007           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3008                  square_color ? WHITENESS : BLACKNESS);\r
3009         } else {\r
3010           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3011         }\r
3012       } \r
3013       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3014           /* [AS] Draw the square using a texture bitmap */\r
3015           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3016           int r = row, c = column; // [HGM] do not flip board in flipView\r
3017           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3018 \r
3019           DrawTile( x, y, \r
3020               squareSize, squareSize, \r
3021               hdc, \r
3022               texture_hdc,\r
3023               backTextureSquareInfo[r][c].mode,\r
3024               backTextureSquareInfo[r][c].x,\r
3025               backTextureSquareInfo[r][c].y );\r
3026 \r
3027           SelectObject( texture_hdc, hbm );\r
3028 \r
3029           if (piece != EmptySquare) {\r
3030               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3031           }\r
3032       }\r
3033       else {\r
3034         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3035 \r
3036         oldBrush = SelectObject(hdc, brush );\r
3037         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3038         SelectObject(hdc, oldBrush);\r
3039         if (piece != EmptySquare)\r
3040           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3041       }\r
3042     }\r
3043   }\r
3044 \r
3045   if( texture_hdc != NULL ) {\r
3046     DeleteDC( texture_hdc );\r
3047   }\r
3048 }\r
3049 \r
3050 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3051 void fputDW(FILE *f, int x)\r
3052 {\r
3053         fputc(x     & 255, f);\r
3054         fputc(x>>8  & 255, f);\r
3055         fputc(x>>16 & 255, f);\r
3056         fputc(x>>24 & 255, f);\r
3057 }\r
3058 \r
3059 #define MAX_CLIPS 200   /* more than enough */\r
3060 \r
3061 VOID\r
3062 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3063 {\r
3064 //  HBITMAP bufferBitmap;\r
3065   BITMAP bi;\r
3066 //  RECT Rect;\r
3067   HDC tmphdc;\r
3068   HBITMAP hbm;\r
3069   int w = 100, h = 50;\r
3070 \r
3071   if(logo == NULL) return;\r
3072 //  GetClientRect(hwndMain, &Rect);\r
3073 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3074 //                                      Rect.bottom-Rect.top+1);\r
3075   tmphdc = CreateCompatibleDC(hdc);\r
3076   hbm = SelectObject(tmphdc, logo);\r
3077   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3078             w = bi.bmWidth;\r
3079             h = bi.bmHeight;\r
3080   }\r
3081   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3082                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3083   SelectObject(tmphdc, hbm);\r
3084   DeleteDC(tmphdc);\r
3085 }\r
3086 \r
3087 VOID\r
3088 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3089 {\r
3090   static Board lastReq, lastDrawn;\r
3091   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3092   static int lastDrawnFlipView = 0;\r
3093   static int lastReqValid = 0, lastDrawnValid = 0;\r
3094   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3095   HDC tmphdc;\r
3096   HDC hdcmem;\r
3097   HBITMAP bufferBitmap;\r
3098   HBITMAP oldBitmap;\r
3099   RECT Rect;\r
3100   HRGN clips[MAX_CLIPS];\r
3101   ChessSquare dragged_piece = EmptySquare;\r
3102 \r
3103   /* I'm undecided on this - this function figures out whether a full\r
3104    * repaint is necessary on its own, so there's no real reason to have the\r
3105    * caller tell it that.  I think this can safely be set to FALSE - but\r
3106    * if we trust the callers not to request full repaints unnessesarily, then\r
3107    * we could skip some clipping work.  In other words, only request a full\r
3108    * redraw when the majority of pieces have changed positions (ie. flip, \r
3109    * gamestart and similar)  --Hawk\r
3110    */\r
3111   Boolean fullrepaint = repaint;\r
3112 \r
3113   if( DrawPositionNeedsFullRepaint() ) {\r
3114       fullrepaint = TRUE;\r
3115   }\r
3116 \r
3117   if (board == NULL) {\r
3118     if (!lastReqValid) {\r
3119       return;\r
3120     }\r
3121     board = lastReq;\r
3122   } else {\r
3123     CopyBoard(lastReq, board);\r
3124     lastReqValid = 1;\r
3125   }\r
3126 \r
3127   if (doingSizing) {\r
3128     return;\r
3129   }\r
3130 \r
3131   if (IsIconic(hwndMain)) {\r
3132     return;\r
3133   }\r
3134 \r
3135   if (hdc == NULL) {\r
3136     hdc = GetDC(hwndMain);\r
3137     if (!appData.monoMode) {\r
3138       SelectPalette(hdc, hPal, FALSE);\r
3139       RealizePalette(hdc);\r
3140     }\r
3141     releaseDC = TRUE;\r
3142   } else {\r
3143     releaseDC = FALSE;\r
3144   }\r
3145 \r
3146   /* Create some work-DCs */\r
3147   hdcmem = CreateCompatibleDC(hdc);\r
3148   tmphdc = CreateCompatibleDC(hdc);\r
3149 \r
3150   /* If dragging is in progress, we temporarely remove the piece */\r
3151   /* [HGM] or temporarily decrease count if stacked              */\r
3152   /*       !! Moved to before board compare !!                   */\r
3153   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3154     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3155     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3156             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3157         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3158     } else \r
3159     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3160             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3161         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3162     } else \r
3163         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3164   }\r
3165 \r
3166   /* Figure out which squares need updating by comparing the \r
3167    * newest board with the last drawn board and checking if\r
3168    * flipping has changed.\r
3169    */\r
3170   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3171     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3172       for (column = 0; column < BOARD_WIDTH; column++) {\r
3173         if (lastDrawn[row][column] != board[row][column]) {\r
3174           SquareToPos(row, column, &x, &y);\r
3175           clips[num_clips++] =\r
3176             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3177         }\r
3178       }\r
3179     }\r
3180     for (i=0; i<2; i++) {\r
3181       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3182           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3183         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3184             lastDrawnHighlight.sq[i].y >= 0) {\r
3185           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3186                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3187           clips[num_clips++] =\r
3188             CreateRectRgn(x - lineGap, y - lineGap, \r
3189                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3190         }\r
3191         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3192           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3193           clips[num_clips++] =\r
3194             CreateRectRgn(x - lineGap, y - lineGap, \r
3195                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3196         }\r
3197       }\r
3198     }\r
3199     for (i=0; i<2; i++) {\r
3200       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3201           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3202         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3203             lastDrawnPremove.sq[i].y >= 0) {\r
3204           SquareToPos(lastDrawnPremove.sq[i].y,\r
3205                       lastDrawnPremove.sq[i].x, &x, &y);\r
3206           clips[num_clips++] =\r
3207             CreateRectRgn(x - lineGap, y - lineGap, \r
3208                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3209         }\r
3210         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3211             premoveHighlightInfo.sq[i].y >= 0) {\r
3212           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3213                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3214           clips[num_clips++] =\r
3215             CreateRectRgn(x - lineGap, y - lineGap, \r
3216                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3217         }\r
3218       }\r
3219     }\r
3220   } else {\r
3221     fullrepaint = TRUE;\r
3222   }\r
3223 \r
3224   /* Create a buffer bitmap - this is the actual bitmap\r
3225    * being written to.  When all the work is done, we can\r
3226    * copy it to the real DC (the screen).  This avoids\r
3227    * the problems with flickering.\r
3228    */\r
3229   GetClientRect(hwndMain, &Rect);\r
3230   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3231                                         Rect.bottom-Rect.top+1);\r
3232   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3233   if (!appData.monoMode) {\r
3234     SelectPalette(hdcmem, hPal, FALSE);\r
3235   }\r
3236 \r
3237   /* Create clips for dragging */\r
3238   if (!fullrepaint) {\r
3239     if (dragInfo.from.x >= 0) {\r
3240       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3241       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3242     }\r
3243     if (dragInfo.start.x >= 0) {\r
3244       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3245       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3246     }\r
3247     if (dragInfo.pos.x >= 0) {\r
3248       x = dragInfo.pos.x - squareSize / 2;\r
3249       y = dragInfo.pos.y - squareSize / 2;\r
3250       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3251     }\r
3252     if (dragInfo.lastpos.x >= 0) {\r
3253       x = dragInfo.lastpos.x - squareSize / 2;\r
3254       y = dragInfo.lastpos.y - squareSize / 2;\r
3255       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3256     }\r
3257   }\r
3258 \r
3259   /* Are we animating a move?  \r
3260    * If so, \r
3261    *   - remove the piece from the board (temporarely)\r
3262    *   - calculate the clipping region\r
3263    */\r
3264   if (!fullrepaint) {\r
3265     if (animInfo.piece != EmptySquare) {\r
3266       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3267       x = boardRect.left + animInfo.lastpos.x;\r
3268       y = boardRect.top + animInfo.lastpos.y;\r
3269       x2 = boardRect.left + animInfo.pos.x;\r
3270       y2 = boardRect.top + animInfo.pos.y;\r
3271       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3272       /* Slight kludge.  The real problem is that after AnimateMove is\r
3273          done, the position on the screen does not match lastDrawn.\r
3274          This currently causes trouble only on e.p. captures in\r
3275          atomic, where the piece moves to an empty square and then\r
3276          explodes.  The old and new positions both had an empty square\r
3277          at the destination, but animation has drawn a piece there and\r
3278          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3279       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3280     }\r
3281   }\r
3282 \r
3283   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3284   if (num_clips == 0)\r
3285     fullrepaint = TRUE;\r
3286 \r
3287   /* Set clipping on the memory DC */\r
3288   if (!fullrepaint) {\r
3289     SelectClipRgn(hdcmem, clips[0]);\r
3290     for (x = 1; x < num_clips; x++) {\r
3291       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3292         abort();  // this should never ever happen!\r
3293     }\r
3294   }\r
3295 \r
3296   /* Do all the drawing to the memory DC */\r
3297   if(explodeInfo.radius) { // [HGM] atomic\r
3298         HBRUSH oldBrush;\r
3299         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3300         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3301         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3302         x += squareSize/2;\r
3303         y += squareSize/2;\r
3304         if(!fullrepaint) {\r
3305           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3306           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3307         }\r
3308         DrawGridOnDC(hdcmem);\r
3309         DrawHighlightsOnDC(hdcmem);\r
3310         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3311         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3312         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3313         SelectObject(hdcmem, oldBrush);\r
3314   } else {\r
3315     DrawGridOnDC(hdcmem);\r
3316     DrawHighlightsOnDC(hdcmem);\r
3317     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3318   }\r
3319   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3320     for (column = 0; column < BOARD_WIDTH; column++) {\r
3321         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3322             HBRUSH oldBrush = SelectObject(hdcmem, \r
3323                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3324             SquareToPos(row, column, &x, &y);\r
3325             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3326                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3327             SelectObject(hdcmem, oldBrush);\r
3328         }\r
3329     }\r
3330   }\r
3331   if(logoHeight) {\r
3332         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3333         if(appData.autoLogo) {\r
3334           \r
3335           switch(gameMode) { // pick logos based on game mode\r
3336             case IcsObserving:\r
3337                 whiteLogo = second.programLogo; // ICS logo\r
3338                 blackLogo = second.programLogo;\r
3339             default:\r
3340                 break;\r
3341             case IcsPlayingWhite:\r
3342                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3343                 blackLogo = second.programLogo; // ICS logo\r
3344                 break;\r
3345             case IcsPlayingBlack:\r
3346                 whiteLogo = second.programLogo; // ICS logo\r
3347                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3348                 break;\r
3349             case TwoMachinesPlay:\r
3350                 if(first.twoMachinesColor[0] == 'b') {\r
3351                     whiteLogo = second.programLogo;\r
3352                     blackLogo = first.programLogo;\r
3353                 }\r
3354                 break;\r
3355             case MachinePlaysWhite:\r
3356                 blackLogo = userLogo;\r
3357                 break;\r
3358             case MachinePlaysBlack:\r
3359                 whiteLogo = userLogo;\r
3360                 blackLogo = first.programLogo;\r
3361           }\r
3362         }\r
3363         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3364         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3365   }\r
3366 \r
3367   if( appData.highlightMoveWithArrow ) {\r
3368     DrawArrowHighlight(hdcmem);\r
3369   }\r
3370 \r
3371   DrawCoordsOnDC(hdcmem);\r
3372 \r
3373   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
3374                  /* to make sure lastDrawn contains what is actually drawn */\r
3375 \r
3376   /* Put the dragged piece back into place and draw it (out of place!) */\r
3377     if (dragged_piece != EmptySquare) {\r
3378     /* [HGM] or restack */\r
3379     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3380                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3381     else\r
3382     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3383                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3384     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3385     x = dragInfo.pos.x - squareSize / 2;\r
3386     y = dragInfo.pos.y - squareSize / 2;\r
3387     DrawPieceOnDC(hdcmem, dragged_piece,\r
3388                   ((int) dragged_piece < (int) BlackPawn), \r
3389                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3390   }   \r
3391   \r
3392   /* Put the animated piece back into place and draw it */\r
3393   if (animInfo.piece != EmptySquare) {\r
3394     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3395     x = boardRect.left + animInfo.pos.x;\r
3396     y = boardRect.top + animInfo.pos.y;\r
3397     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3398                   ((int) animInfo.piece < (int) BlackPawn),\r
3399                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3400   }\r
3401 \r
3402   /* Release the bufferBitmap by selecting in the old bitmap \r
3403    * and delete the memory DC\r
3404    */\r
3405   SelectObject(hdcmem, oldBitmap);\r
3406   DeleteDC(hdcmem);\r
3407 \r
3408   /* Set clipping on the target DC */\r
3409   if (!fullrepaint) {\r
3410     SelectClipRgn(hdc, clips[0]);\r
3411     for (x = 1; x < num_clips; x++) {\r
3412       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3413         abort();   // this should never ever happen!\r
3414     } \r
3415   }\r
3416 \r
3417   /* Copy the new bitmap onto the screen in one go.\r
3418    * This way we avoid any flickering\r
3419    */\r
3420   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3421   BitBlt(hdc, boardRect.left, boardRect.top,\r
3422          boardRect.right - boardRect.left,\r
3423          boardRect.bottom - boardRect.top,\r
3424          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3425   if(saveDiagFlag) { \r
3426     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3427     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3428 \r
3429     GetObject(bufferBitmap, sizeof(b), &b);\r
3430     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3431         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3432         bih.biWidth = b.bmWidth;\r
3433         bih.biHeight = b.bmHeight;\r
3434         bih.biPlanes = 1;\r
3435         bih.biBitCount = b.bmBitsPixel;\r
3436         bih.biCompression = 0;\r
3437         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3438         bih.biXPelsPerMeter = 0;\r
3439         bih.biYPelsPerMeter = 0;\r
3440         bih.biClrUsed = 0;\r
3441         bih.biClrImportant = 0;\r
3442 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3443 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3444         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3445 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3446 \r
3447         wb = b.bmWidthBytes;\r
3448         // count colors\r
3449         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3450                 int k = ((int*) pData)[i];\r
3451                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3452                 if(j >= 16) break;\r
3453                 color[j] = k;\r
3454                 if(j >= nrColors) nrColors = j+1;\r
3455         }\r
3456         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3457                 INT p = 0;\r
3458                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3459                     for(w=0; w<(wb>>2); w+=2) {\r
3460                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3461                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3462                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3463                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3464                         pData[p++] = m | j<<4;\r
3465                     }\r
3466                     while(p&3) pData[p++] = 0;\r
3467                 }\r
3468                 fac = 3;\r
3469                 wb = ((wb+31)>>5)<<2;\r
3470         }\r
3471         // write BITMAPFILEHEADER\r
3472         fprintf(diagFile, "BM");\r
3473         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3474         fputDW(diagFile, 0);\r
3475         fputDW(diagFile, 0x36 + (fac?64:0));\r
3476         // write BITMAPINFOHEADER\r
3477         fputDW(diagFile, 40);\r
3478         fputDW(diagFile, b.bmWidth);\r
3479         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3480         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3481         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3482         fputDW(diagFile, 0);\r
3483         fputDW(diagFile, 0);\r
3484         fputDW(diagFile, 0);\r
3485         fputDW(diagFile, 0);\r
3486         fputDW(diagFile, 0);\r
3487         fputDW(diagFile, 0);\r
3488         // write color table\r
3489         if(fac)\r
3490         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3491         // write bitmap data\r
3492         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3493                 fputc(pData[i], diagFile);\r
3494      }\r
3495   }\r
3496 \r
3497   SelectObject(tmphdc, oldBitmap);\r
3498 \r
3499   /* Massive cleanup */\r
3500   for (x = 0; x < num_clips; x++)\r
3501     DeleteObject(clips[x]);\r
3502 \r
3503   DeleteDC(tmphdc);\r
3504   DeleteObject(bufferBitmap);\r
3505 \r
3506   if (releaseDC) \r
3507     ReleaseDC(hwndMain, hdc);\r
3508   \r
3509   if (lastDrawnFlipView != flipView) {\r
3510     if (flipView)\r
3511       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3512     else\r
3513       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3514   }\r
3515 \r
3516 /*  CopyBoard(lastDrawn, board);*/\r
3517   lastDrawnHighlight = highlightInfo;\r
3518   lastDrawnPremove   = premoveHighlightInfo;\r
3519   lastDrawnFlipView = flipView;\r
3520   lastDrawnValid = 1;\r
3521 }\r
3522 \r
3523 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3524 int\r
3525 SaveDiagram(f)\r
3526      FILE *f;\r
3527 {\r
3528     saveDiagFlag = 1; diagFile = f;\r
3529     HDCDrawPosition(NULL, TRUE, NULL);\r
3530 \r
3531     saveDiagFlag = 0;\r
3532 \r
3533 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3534     \r
3535     fclose(f);\r
3536     return TRUE;\r
3537 }\r
3538 \r
3539 \r
3540 /*---------------------------------------------------------------------------*\\r
3541 | CLIENT PAINT PROCEDURE\r
3542 |   This is the main event-handler for the WM_PAINT message.\r
3543 |\r
3544 \*---------------------------------------------------------------------------*/\r
3545 VOID\r
3546 PaintProc(HWND hwnd)\r
3547 {\r
3548   HDC         hdc;\r
3549   PAINTSTRUCT ps;\r
3550   HFONT       oldFont;\r
3551 \r
3552   if((hdc = BeginPaint(hwnd, &ps))) {\r
3553     if (IsIconic(hwnd)) {\r
3554       DrawIcon(hdc, 2, 2, iconCurrent);\r
3555     } else {\r
3556       if (!appData.monoMode) {\r
3557         SelectPalette(hdc, hPal, FALSE);\r
3558         RealizePalette(hdc);\r
3559       }\r
3560       HDCDrawPosition(hdc, 1, NULL);\r
3561       oldFont =\r
3562         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3563       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3564                  ETO_CLIPPED|ETO_OPAQUE,\r
3565                  &messageRect, messageText, strlen(messageText), NULL);\r
3566       SelectObject(hdc, oldFont);\r
3567       DisplayBothClocks();\r
3568     }\r
3569     EndPaint(hwnd,&ps);\r
3570   }\r
3571 \r
3572   return;\r
3573 }\r
3574 \r
3575 \r
3576 /*\r
3577  * If the user selects on a border boundary, return -1; if off the board,\r
3578  *   return -2.  Otherwise map the event coordinate to the square.\r
3579  * The offset boardRect.left or boardRect.top must already have been\r
3580  *   subtracted from x.\r
3581  */\r
3582 int EventToSquare(x, limit)\r
3583      int x, limit;\r
3584 {\r
3585   if (x <= 0)\r
3586     return -2;\r
3587   if (x < lineGap)\r
3588     return -1;\r
3589   x -= lineGap;\r
3590   if ((x % (squareSize + lineGap)) >= squareSize)\r
3591     return -1;\r
3592   x /= (squareSize + lineGap);\r
3593     if (x >= limit)\r
3594     return -2;\r
3595   return x;\r
3596 }\r
3597 \r
3598 typedef struct {\r
3599   char piece;\r
3600   int command;\r
3601   char* name;\r
3602 } DropEnable;\r
3603 \r
3604 DropEnable dropEnables[] = {\r
3605   { 'P', DP_Pawn, "Pawn" },\r
3606   { 'N', DP_Knight, "Knight" },\r
3607   { 'B', DP_Bishop, "Bishop" },\r
3608   { 'R', DP_Rook, "Rook" },\r
3609   { 'Q', DP_Queen, "Queen" },\r
3610 };\r
3611 \r
3612 VOID\r
3613 SetupDropMenu(HMENU hmenu)\r
3614 {\r
3615   int i, count, enable;\r
3616   char *p;\r
3617   extern char white_holding[], black_holding[];\r
3618   char item[MSG_SIZ];\r
3619 \r
3620   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3621     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3622                dropEnables[i].piece);\r
3623     count = 0;\r
3624     while (p && *p++ == dropEnables[i].piece) count++;\r
3625     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
3626     enable = count > 0 || !appData.testLegality\r
3627       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3628                       && !appData.icsActive);\r
3629     ModifyMenu(hmenu, dropEnables[i].command,\r
3630                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3631                dropEnables[i].command, item);\r
3632   }\r
3633 }\r
3634 \r
3635 void DragPieceBegin(int x, int y)\r
3636 {\r
3637       dragInfo.lastpos.x = boardRect.left + x;\r
3638       dragInfo.lastpos.y = boardRect.top + y;\r
3639       dragInfo.from.x = fromX;\r
3640       dragInfo.from.y = fromY;\r
3641       dragInfo.start = dragInfo.from;\r
3642       SetCapture(hwndMain);\r
3643 }\r
3644 \r
3645 void DragPieceEnd(int x, int y)\r
3646 {\r
3647     ReleaseCapture();\r
3648     dragInfo.start.x = dragInfo.start.y = -1;\r
3649     dragInfo.from = dragInfo.start;\r
3650     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3651 }\r
3652 \r
3653 /* Event handler for mouse messages */\r
3654 VOID\r
3655 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3656 {\r
3657   int x, y, menuNr;\r
3658   POINT pt;\r
3659   static int recursive = 0;\r
3660   HMENU hmenu;\r
3661   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
3662 \r
3663   if (recursive) {\r
3664     if (message == WM_MBUTTONUP) {\r
3665       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
3666          to the middle button: we simulate pressing the left button too!\r
3667          */\r
3668       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
3669       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
3670     }\r
3671     return;\r
3672   }\r
3673   recursive++;\r
3674   \r
3675   pt.x = LOWORD(lParam);\r
3676   pt.y = HIWORD(lParam);\r
3677   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
3678   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
3679   if (!flipView && y >= 0) {\r
3680     y = BOARD_HEIGHT - 1 - y;\r
3681   }\r
3682   if (flipView && x >= 0) {\r
3683     x = BOARD_WIDTH - 1 - x;\r
3684   }\r
3685 \r
3686   switch (message) {\r
3687   case WM_LBUTTONDOWN:\r
3688       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3689         if (gameMode == EditPosition) {\r
3690           SetWhiteToPlayEvent();\r
3691         } else if (gameMode == IcsPlayingBlack ||\r
3692                    gameMode == MachinePlaysWhite) {\r
3693           CallFlagEvent();\r
3694         } else if (gameMode == EditGame) {\r
3695           AdjustClock(flipClock, -1);\r
3696         }\r
3697       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3698         if (gameMode == EditPosition) {\r
3699           SetBlackToPlayEvent();\r
3700         } else if (gameMode == IcsPlayingWhite ||\r
3701                    gameMode == MachinePlaysBlack) {\r
3702           CallFlagEvent();\r
3703         } else if (gameMode == EditGame) {\r
3704           AdjustClock(!flipClock, -1);\r
3705         }\r
3706       }\r
3707       dragInfo.start.x = dragInfo.start.y = -1;\r
3708       dragInfo.from = dragInfo.start;\r
3709     if(fromX == -1 && frozen) { // not sure where this is for\r
3710                 fromX = fromY = -1; \r
3711       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
3712       break;\r
3713     }\r
3714       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
3715       DrawPosition(TRUE, NULL);\r
3716     break;\r
3717 \r
3718   case WM_LBUTTONUP:\r
3719       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
3720       DrawPosition(TRUE, NULL);\r
3721     break;\r
3722 \r
3723   case WM_MOUSEMOVE:\r
3724     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
3725     if ((appData.animateDragging || appData.highlightDragging)\r
3726         && (wParam & MK_LBUTTON)\r
3727         && dragInfo.from.x >= 0) \r
3728     {\r
3729       BOOL full_repaint = FALSE;\r
3730 \r
3731       if (appData.animateDragging) {\r
3732         dragInfo.pos = pt;\r
3733       }\r
3734       if (appData.highlightDragging) {\r
3735         SetHighlights(fromX, fromY, x, y);\r
3736         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
3737             full_repaint = TRUE;\r
3738         }\r
3739       }\r
3740       \r
3741       DrawPosition( full_repaint, NULL);\r
3742       \r
3743       dragInfo.lastpos = dragInfo.pos;\r
3744     }\r
3745     break;\r
3746 \r
3747   case WM_MOUSEWHEEL: // [DM]\r
3748     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
3749        /* Mouse Wheel is being rolled forward\r
3750         * Play moves forward\r
3751         */\r
3752        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
3753                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
3754        /* Mouse Wheel is being rolled backward\r
3755         * Play moves backward\r
3756         */\r
3757        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
3758                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
3759     }\r
3760     break;\r
3761 \r
3762   case WM_MBUTTONUP:\r
3763   case WM_RBUTTONUP:\r
3764     ReleaseCapture();\r
3765     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
3766     break;\r
3767  \r
3768   case WM_MBUTTONDOWN:\r
3769   case WM_RBUTTONDOWN:\r
3770     ErrorPopDown();\r
3771     ReleaseCapture();\r
3772     fromX = fromY = -1;\r
3773     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3774     dragInfo.start.x = dragInfo.start.y = -1;\r
3775     dragInfo.from = dragInfo.start;\r
3776     dragInfo.lastpos = dragInfo.pos;\r
3777     if (appData.highlightDragging) {\r
3778       ClearHighlights();\r
3779     }\r
3780     if(y == -2) {\r
3781       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
3782       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3783           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
3784       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3785           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
3786       }\r
3787     }\r
3788     DrawPosition(TRUE, NULL);\r
3789 \r
3790     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
3791     switch (menuNr) {\r
3792     case 0:\r
3793       if (message == WM_MBUTTONDOWN) {\r
3794         buttonCount = 3;  /* even if system didn't think so */\r
3795         if (wParam & MK_SHIFT) \r
3796           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3797         else\r
3798           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3799       } else { /* message == WM_RBUTTONDOWN */\r
3800         /* Just have one menu, on the right button.  Windows users don't\r
3801            think to try the middle one, and sometimes other software steals\r
3802            it, or it doesn't really exist. */\r
3803         if(gameInfo.variant != VariantShogi)\r
3804             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3805         else\r
3806             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
3807       }\r
3808       break;\r
3809     case 2:\r
3810       SetCapture(hwndMain);
3811       break;\r
3812     case 1:\r
3813       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3814       SetupDropMenu(hmenu);\r
3815       MenuPopup(hwnd, pt, hmenu, -1);\r
3816     default:\r
3817       break;\r
3818     }\r
3819     break;\r
3820   }\r
3821 \r
3822   recursive--;\r
3823 }\r
3824 \r
3825 /* Preprocess messages for buttons in main window */\r
3826 LRESULT CALLBACK\r
3827 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3828 {\r
3829   int id = GetWindowLong(hwnd, GWL_ID);\r
3830   int i, dir;\r
3831 \r
3832   for (i=0; i<N_BUTTONS; i++) {\r
3833     if (buttonDesc[i].id == id) break;\r
3834   }\r
3835   if (i == N_BUTTONS) return 0;\r
3836   switch (message) {\r
3837   case WM_KEYDOWN:\r
3838     switch (wParam) {\r
3839     case VK_LEFT:\r
3840     case VK_RIGHT:\r
3841       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3842       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3843       return TRUE;\r
3844     }\r
3845     break;\r
3846   case WM_CHAR:\r
3847     switch (wParam) {\r
3848     case '\r':\r
3849       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3850       return TRUE;\r
3851     default:\r
3852       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
3853         // [HGM] movenum: only letters or leading zero should go to ICS input\r
3854         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3855         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3856         SetFocus(h);\r
3857         SendMessage(h, WM_CHAR, wParam, lParam);\r
3858         return TRUE;\r
3859       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3860         PopUpMoveDialog((char)wParam);\r
3861       }\r
3862       break;\r
3863     }\r
3864     break;\r
3865   }\r
3866   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3867 }\r
3868 \r
3869 /* Process messages for Promotion dialog box */\r
3870 LRESULT CALLBACK\r
3871 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3872 {\r
3873   char promoChar;\r
3874 \r
3875   switch (message) {\r
3876   case WM_INITDIALOG: /* message: initialize dialog box */\r
3877     /* Center the dialog over the application window */\r
3878     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
3879     ShowWindow(GetDlgItem(hDlg, PB_King), \r
3880       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
3881        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
3882                SW_SHOW : SW_HIDE);\r
3883     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
3884     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
3885        ((PieceToChar(WhiteAngel) >= 'A' &&\r
3886          PieceToChar(WhiteAngel) != '~') ||\r
3887         (PieceToChar(BlackAngel) >= 'A' &&\r
3888          PieceToChar(BlackAngel) != '~')   ) ?\r
3889                SW_SHOW : SW_HIDE);\r
3890     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
3891        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
3892          PieceToChar(WhiteMarshall) != '~') ||\r
3893         (PieceToChar(BlackMarshall) >= 'A' &&\r
3894          PieceToChar(BlackMarshall) != '~')   ) ?\r
3895                SW_SHOW : SW_HIDE);\r
3896     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
3897     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
3898        gameInfo.variant != VariantShogi ?\r
3899                SW_SHOW : SW_HIDE);\r
3900     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
3901        gameInfo.variant != VariantShogi ?\r
3902                SW_SHOW : SW_HIDE);\r
3903     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
3904        gameInfo.variant == VariantShogi ?\r
3905                SW_SHOW : SW_HIDE);\r
3906     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
3907        gameInfo.variant == VariantShogi ?\r
3908                SW_SHOW : SW_HIDE);\r
3909     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
3910        gameInfo.variant == VariantSuper ?\r
3911                SW_SHOW : SW_HIDE);\r
3912     return TRUE;\r
3913 \r
3914   case WM_COMMAND: /* message: received a command */\r
3915     switch (LOWORD(wParam)) {\r
3916     case IDCANCEL:\r
3917       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3918       ClearHighlights();\r
3919       DrawPosition(FALSE, NULL);\r
3920       return TRUE;\r
3921     case PB_King:\r
3922       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
3923       break;\r
3924     case PB_Queen:\r
3925       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
3926       break;\r
3927     case PB_Rook:\r
3928       promoChar = PieceToChar(BlackRook);\r
3929       break;\r
3930     case PB_Bishop:\r
3931       promoChar = PieceToChar(BlackBishop);\r
3932       break;\r
3933     case PB_Chancellor:\r
3934       promoChar = PieceToChar(BlackMarshall);\r
3935       break;\r
3936     case PB_Archbishop:\r
3937       promoChar = PieceToChar(BlackAngel);\r
3938       break;\r
3939     case PB_Knight:\r
3940       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
3941       break;\r
3942     default:\r
3943       return FALSE;\r
3944     }\r
3945     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3946     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
3947        only show the popup when we are already sure the move is valid or\r
3948        legal. We pass a faulty move type, but the kludge is that FinishMove\r
3949        will figure out it is a promotion from the promoChar. */\r
3950     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3951     fromX = fromY = -1;\r
3952     if (!appData.highlightLastMove) {\r
3953       ClearHighlights();\r
3954       DrawPosition(FALSE, NULL);\r
3955     }\r
3956     return TRUE;\r
3957   }\r
3958   return FALSE;\r
3959 }\r
3960 \r
3961 /* Pop up promotion dialog */\r
3962 VOID\r
3963 PromotionPopup(HWND hwnd)\r
3964 {\r
3965   FARPROC lpProc;\r
3966 \r
3967   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3968   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3969     hwnd, (DLGPROC)lpProc);\r
3970   FreeProcInstance(lpProc);\r
3971 }\r
3972 \r
3973 void\r
3974 PromotionPopUp()\r
3975 {\r
3976   DrawPosition(TRUE, NULL);\r
3977   PromotionPopup(hwndMain);\r
3978 }\r
3979 \r
3980 /* Toggle ShowThinking */\r
3981 VOID\r
3982 ToggleShowThinking()\r
3983 {\r
3984   appData.showThinking = !appData.showThinking;\r
3985   ShowThinkingEvent();\r
3986 }\r
3987 \r
3988 VOID\r
3989 LoadGameDialog(HWND hwnd, char* title)\r
3990 {\r
3991   UINT number = 0;\r
3992   FILE *f;\r
3993   char fileTitle[MSG_SIZ];\r
3994   f = OpenFileDialog(hwnd, "rb", "",\r
3995                      appData.oldSaveStyle ? "gam" : "pgn",\r
3996                      GAME_FILT,\r
3997                      title, &number, fileTitle, NULL);\r
3998   if (f != NULL) {\r
3999     cmailMsgLoaded = FALSE;\r
4000     if (number == 0) {\r
4001       int error = GameListBuild(f);\r
4002       if (error) {\r
4003         DisplayError("Cannot build game list", error);\r
4004       } else if (!ListEmpty(&gameList) &&\r
4005                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4006         GameListPopUp(f, fileTitle);\r
4007         return;\r
4008       }\r
4009       GameListDestroy();\r
4010       number = 1;\r
4011     }\r
4012     LoadGame(f, number, fileTitle, FALSE);\r
4013   }\r
4014 }\r
4015 \r
4016 int get_term_width()\r
4017 {\r
4018     HDC hdc;\r
4019     TEXTMETRIC tm;\r
4020     RECT rc;\r
4021     HFONT hfont, hold_font;\r
4022     LOGFONT lf;\r
4023     HWND hText;\r
4024 \r
4025     if (hwndConsole)\r
4026         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4027     else\r
4028         return 79;\r
4029 \r
4030     // get the text metrics\r
4031     hdc = GetDC(hText);\r
4032     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4033     if (consoleCF.dwEffects & CFE_BOLD)\r
4034         lf.lfWeight = FW_BOLD;\r
4035     if (consoleCF.dwEffects & CFE_ITALIC)\r
4036         lf.lfItalic = TRUE;\r
4037     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4038         lf.lfStrikeOut = TRUE;\r
4039     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4040         lf.lfUnderline = TRUE;\r
4041     hfont = CreateFontIndirect(&lf);\r
4042     hold_font = SelectObject(hdc, hfont);\r
4043     GetTextMetrics(hdc, &tm);\r
4044     SelectObject(hdc, hold_font);\r
4045     DeleteObject(hfont);\r
4046     ReleaseDC(hText, hdc);\r
4047 \r
4048     // get the rectangle\r
4049     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4050 \r
4051     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4052 }\r
4053 \r
4054 void UpdateICSWidth(HWND hText)\r
4055 {\r
4056     LONG old_width, new_width;\r
4057 \r
4058     new_width = get_term_width(hText, FALSE);\r
4059     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4060     if (new_width != old_width)\r
4061     {\r
4062         ics_update_width(new_width);\r
4063         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4064     }\r
4065 }\r
4066 \r
4067 VOID\r
4068 ChangedConsoleFont()\r
4069 {\r
4070   CHARFORMAT cfmt;\r
4071   CHARRANGE tmpsel, sel;\r
4072   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4073   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4074   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4075   PARAFORMAT paraf;\r
4076 \r
4077   cfmt.cbSize = sizeof(CHARFORMAT);\r
4078   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4079   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4080   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4081    * size.  This was undocumented in the version of MSVC++ that I had\r
4082    * when I wrote the code, but is apparently documented now.\r
4083    */\r
4084   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4085   cfmt.bCharSet = f->lf.lfCharSet;\r
4086   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4087   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4088   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4089   /* Why are the following seemingly needed too? */\r
4090   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4091   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4092   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4093   tmpsel.cpMin = 0;\r
4094   tmpsel.cpMax = -1; /*999999?*/\r
4095   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4096   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4097   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4098    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4099    */\r
4100   paraf.cbSize = sizeof(paraf);\r
4101   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4102   paraf.dxStartIndent = 0;\r
4103   paraf.dxOffset = WRAP_INDENT;\r
4104   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4105   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4106   UpdateICSWidth(hText);\r
4107 }\r
4108 \r
4109 /*---------------------------------------------------------------------------*\\r
4110  *\r
4111  * Window Proc for main window\r
4112  *\r
4113 \*---------------------------------------------------------------------------*/\r
4114 \r
4115 /* Process messages for main window, etc. */\r
4116 LRESULT CALLBACK\r
4117 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4118 {\r
4119   FARPROC lpProc;\r
4120   int wmId, wmEvent;\r
4121   char *defName;\r
4122   FILE *f;\r
4123   UINT number;\r
4124   char fileTitle[MSG_SIZ];\r
4125   char buf[MSG_SIZ];\r
4126   static SnapData sd;\r
4127 \r
4128   switch (message) {\r
4129 \r
4130   case WM_PAINT: /* message: repaint portion of window */\r
4131     PaintProc(hwnd);\r
4132     break;\r
4133 \r
4134   case WM_ERASEBKGND:\r
4135     if (IsIconic(hwnd)) {\r
4136       /* Cheat; change the message */\r
4137       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4138     } else {\r
4139       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4140     }\r
4141     break;\r
4142 \r
4143   case WM_LBUTTONDOWN:\r
4144   case WM_MBUTTONDOWN:\r
4145   case WM_RBUTTONDOWN:\r
4146   case WM_LBUTTONUP:\r
4147   case WM_MBUTTONUP:\r
4148   case WM_RBUTTONUP:\r
4149   case WM_MOUSEMOVE:\r
4150   case WM_MOUSEWHEEL:\r
4151     MouseEvent(hwnd, message, wParam, lParam);\r
4152     break;\r
4153 \r
4154   JAWS_KB_NAVIGATION\r
4155 \r
4156   case WM_CHAR:\r
4157     \r
4158     JAWS_ALT_INTERCEPT\r
4159 \r
4160     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4161         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4162         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4163         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4164         SetFocus(h);\r
4165         SendMessage(h, message, wParam, lParam);\r
4166     } else if(lParam != KF_REPEAT) {\r
4167         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4168                 PopUpMoveDialog((char)wParam);\r
4169         } else if((char)wParam == 003) CopyGameToClipboard();\r
4170          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4171     }\r
4172 \r
4173     break;\r
4174 \r
4175   case WM_PALETTECHANGED:\r
4176     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4177       int nnew;\r
4178       HDC hdc = GetDC(hwndMain);\r
4179       SelectPalette(hdc, hPal, TRUE);\r
4180       nnew = RealizePalette(hdc);\r
4181       if (nnew > 0) {\r
4182         paletteChanged = TRUE;\r
4183         InvalidateRect(hwnd, &boardRect, FALSE);\r
4184       }\r
4185       ReleaseDC(hwnd, hdc);\r
4186     }\r
4187     break;\r
4188 \r
4189   case WM_QUERYNEWPALETTE:\r
4190     if (!appData.monoMode /*&& paletteChanged*/) {\r
4191       int nnew;\r
4192       HDC hdc = GetDC(hwndMain);\r
4193       paletteChanged = FALSE;\r
4194       SelectPalette(hdc, hPal, FALSE);\r
4195       nnew = RealizePalette(hdc);\r
4196       if (nnew > 0) {\r
4197         InvalidateRect(hwnd, &boardRect, FALSE);\r
4198       }\r
4199       ReleaseDC(hwnd, hdc);\r
4200       return TRUE;\r
4201     }\r
4202     return FALSE;\r
4203 \r
4204   case WM_COMMAND: /* message: command from application menu */\r
4205     wmId    = LOWORD(wParam);\r
4206     wmEvent = HIWORD(wParam);\r
4207 \r
4208     switch (wmId) {\r
4209     case IDM_NewGame:\r
4210       ResetGameEvent();\r
4211       SAY("new game enter a move to play against the computer with white");\r
4212       break;\r
4213 \r
4214     case IDM_NewGameFRC:\r
4215       if( NewGameFRC() == 0 ) {\r
4216         ResetGameEvent();\r
4217       }\r
4218       break;\r
4219 \r
4220     case IDM_NewVariant:\r
4221       NewVariantPopup(hwnd);\r
4222       break;\r
4223 \r
4224     case IDM_LoadGame:\r
4225       LoadGameDialog(hwnd, "Load Game from File");\r
4226       break;\r
4227 \r
4228     case IDM_LoadNextGame:\r
4229       ReloadGame(1);\r
4230       break;\r
4231 \r
4232     case IDM_LoadPrevGame:\r
4233       ReloadGame(-1);\r
4234       break;\r
4235 \r
4236     case IDM_ReloadGame:\r
4237       ReloadGame(0);\r
4238       break;\r
4239 \r
4240     case IDM_LoadPosition:\r
4241       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4242         Reset(FALSE, TRUE);\r
4243       }\r
4244       number = 1;\r
4245       f = OpenFileDialog(hwnd, "rb", "",\r
4246                          appData.oldSaveStyle ? "pos" : "fen",\r
4247                          POSITION_FILT,\r
4248                          "Load Position from File", &number, fileTitle, NULL);\r
4249       if (f != NULL) {\r
4250         LoadPosition(f, number, fileTitle);\r
4251       }\r
4252       break;\r
4253 \r
4254     case IDM_LoadNextPosition:\r
4255       ReloadPosition(1);\r
4256       break;\r
4257 \r
4258     case IDM_LoadPrevPosition:\r
4259       ReloadPosition(-1);\r
4260       break;\r
4261 \r
4262     case IDM_ReloadPosition:\r
4263       ReloadPosition(0);\r
4264       break;\r
4265 \r
4266     case IDM_SaveGame:\r
4267       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4268       f = OpenFileDialog(hwnd, "a", defName,\r
4269                          appData.oldSaveStyle ? "gam" : "pgn",\r
4270                          GAME_FILT,\r
4271                          "Save Game to File", NULL, fileTitle, NULL);\r
4272       if (f != NULL) {\r
4273         SaveGame(f, 0, "");\r
4274       }\r
4275       break;\r
4276 \r
4277     case IDM_SavePosition:\r
4278       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4279       f = OpenFileDialog(hwnd, "a", defName,\r
4280                          appData.oldSaveStyle ? "pos" : "fen",\r
4281                          POSITION_FILT,\r
4282                          "Save Position to File", NULL, fileTitle, NULL);\r
4283       if (f != NULL) {\r
4284         SavePosition(f, 0, "");\r
4285       }\r
4286       break;\r
4287 \r
4288     case IDM_SaveDiagram:\r
4289       defName = "diagram";\r
4290       f = OpenFileDialog(hwnd, "wb", defName,\r
4291                          "bmp",\r
4292                          DIAGRAM_FILT,\r
4293                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4294       if (f != NULL) {\r
4295         SaveDiagram(f);\r
4296       }\r
4297       break;\r
4298 \r
4299     case IDM_CopyGame:\r
4300       CopyGameToClipboard();\r
4301       break;\r
4302 \r
4303     case IDM_PasteGame:\r
4304       PasteGameFromClipboard();\r
4305       break;\r
4306 \r
4307     case IDM_CopyGameListToClipboard:\r
4308       CopyGameListToClipboard();\r
4309       break;\r
4310 \r
4311     /* [AS] Autodetect FEN or PGN data */\r
4312     case IDM_PasteAny:\r
4313       PasteGameOrFENFromClipboard();\r
4314       break;\r
4315 \r
4316     /* [AS] Move history */\r
4317     case IDM_ShowMoveHistory:\r
4318         if( MoveHistoryIsUp() ) {\r
4319             MoveHistoryPopDown();\r
4320         }\r
4321         else {\r
4322             MoveHistoryPopUp();\r
4323         }\r
4324         break;\r
4325 \r
4326     /* [AS] Eval graph */\r
4327     case IDM_ShowEvalGraph:\r
4328         if( EvalGraphIsUp() ) {\r
4329             EvalGraphPopDown();\r
4330         }\r
4331         else {\r
4332             EvalGraphPopUp();\r
4333             SetFocus(hwndMain);\r
4334         }\r
4335         break;\r
4336 \r
4337     /* [AS] Engine output */\r
4338     case IDM_ShowEngineOutput:\r
4339         if( EngineOutputIsUp() ) {\r
4340             EngineOutputPopDown();\r
4341         }\r
4342         else {\r
4343             EngineOutputPopUp();\r
4344         }\r
4345         break;\r
4346 \r
4347     /* [AS] User adjudication */\r
4348     case IDM_UserAdjudication_White:\r
4349         UserAdjudicationEvent( +1 );\r
4350         break;\r
4351 \r
4352     case IDM_UserAdjudication_Black:\r
4353         UserAdjudicationEvent( -1 );\r
4354         break;\r
4355 \r
4356     case IDM_UserAdjudication_Draw:\r
4357         UserAdjudicationEvent( 0 );\r
4358         break;\r
4359 \r
4360     /* [AS] Game list options dialog */\r
4361     case IDM_GameListOptions:\r
4362       GameListOptions();\r
4363       break;\r
4364 \r
4365     case IDM_NewChat:\r
4366       ChatPopUp();\r
4367       break;\r
4368 \r
4369     case IDM_CopyPosition:\r
4370       CopyFENToClipboard();\r
4371       break;\r
4372 \r
4373     case IDM_PastePosition:\r
4374       PasteFENFromClipboard();\r
4375       break;\r
4376 \r
4377     case IDM_MailMove:\r
4378       MailMoveEvent();\r
4379       break;\r
4380 \r
4381     case IDM_ReloadCMailMsg:\r
4382       Reset(TRUE, TRUE);\r
4383       ReloadCmailMsgEvent(FALSE);\r
4384       break;\r
4385 \r
4386     case IDM_Minimize:\r
4387       ShowWindow(hwnd, SW_MINIMIZE);\r
4388       break;\r
4389 \r
4390     case IDM_Exit:\r
4391       ExitEvent(0);\r
4392       break;\r
4393 \r
4394     case IDM_MachineWhite:\r
4395       MachineWhiteEvent();\r
4396       /*\r
4397        * refresh the tags dialog only if it's visible\r
4398        */\r
4399       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4400           char *tags;\r
4401           tags = PGNTags(&gameInfo);\r
4402           TagsPopUp(tags, CmailMsg());\r
4403           free(tags);\r
4404       }\r
4405       SAY("computer starts playing white");\r
4406       break;\r
4407 \r
4408     case IDM_MachineBlack:\r
4409       MachineBlackEvent();\r
4410       /*\r
4411        * refresh the tags dialog only if it's visible\r
4412        */\r
4413       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4414           char *tags;\r
4415           tags = PGNTags(&gameInfo);\r
4416           TagsPopUp(tags, CmailMsg());\r
4417           free(tags);\r
4418       }\r
4419       SAY("computer starts playing black");\r
4420       break;\r
4421 \r
4422     case IDM_TwoMachines:\r
4423       TwoMachinesEvent();\r
4424       /*\r
4425        * refresh the tags dialog only if it's visible\r
4426        */\r
4427       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4428           char *tags;\r
4429           tags = PGNTags(&gameInfo);\r
4430           TagsPopUp(tags, CmailMsg());\r
4431           free(tags);\r
4432       }\r
4433       SAY("programs start playing each other");\r
4434       break;\r
4435 \r
4436     case IDM_AnalysisMode:\r
4437       if (!first.analysisSupport) {\r
4438         sprintf(buf, "%s does not support analysis", first.tidy);\r
4439         DisplayError(buf, 0);\r
4440       } else {\r
4441         SAY("analyzing current position");\r
4442         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4443         if (appData.icsActive) {\r
4444                if (gameMode != IcsObserving) {\r
4445                        sprintf(buf, "You are not observing a game");\r
4446                        DisplayError(buf, 0);\r
4447                        /* secure check */\r
4448                        if (appData.icsEngineAnalyze) {\r
4449                                if (appData.debugMode) \r
4450                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4451                                ExitAnalyzeMode();\r
4452                                ModeHighlight();\r
4453                                break;\r
4454                        }\r
4455                        break;\r
4456                } else {\r
4457                        /* if enable, user want disable icsEngineAnalyze */\r
4458                        if (appData.icsEngineAnalyze) {\r
4459                                ExitAnalyzeMode();\r
4460                                ModeHighlight();\r
4461                                break;\r
4462                        }\r
4463                        appData.icsEngineAnalyze = TRUE;\r
4464                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4465                }\r
4466         } \r
4467         if (!appData.showThinking) ToggleShowThinking();\r
4468         AnalyzeModeEvent();\r
4469       }\r
4470       break;\r
4471 \r
4472     case IDM_AnalyzeFile:\r
4473       if (!first.analysisSupport) {\r
4474         char buf[MSG_SIZ];\r
4475         sprintf(buf, "%s does not support analysis", first.tidy);\r
4476         DisplayError(buf, 0);\r
4477       } else {\r
4478         if (!appData.showThinking) ToggleShowThinking();\r
4479         AnalyzeFileEvent();\r
4480         LoadGameDialog(hwnd, "Analyze Game from File");\r
4481         AnalysisPeriodicEvent(1);\r
4482       }\r
4483       break;\r
4484 \r
4485     case IDM_IcsClient:\r
4486       IcsClientEvent();\r
4487       break;\r
4488 \r
4489     case IDM_EditGame:\r
4490       EditGameEvent();\r
4491       SAY("edit game");\r
4492       break;\r
4493 \r
4494     case IDM_EditPosition:\r
4495       EditPositionEvent();\r
4496       SAY("to set up a position type a FEN");\r
4497       break;\r
4498 \r
4499     case IDM_Training:\r
4500       TrainingEvent();\r
4501       break;\r
4502 \r
4503     case IDM_ShowGameList:\r
4504       ShowGameListProc();\r
4505       break;\r
4506 \r
4507     case IDM_EditTags:\r
4508       EditTagsProc();\r
4509       break;\r
4510 \r
4511     case IDM_EditComment:\r
4512       if (commentUp && editComment) {\r
4513         CommentPopDown();\r
4514       } else {\r
4515         EditCommentEvent();\r
4516       }\r
4517       break;\r
4518 \r
4519     case IDM_Pause:\r
4520       PauseEvent();\r
4521       break;\r
4522 \r
4523     case IDM_Accept:\r
4524       AcceptEvent();\r
4525       break;\r
4526 \r
4527     case IDM_Decline:\r
4528       DeclineEvent();\r
4529       break;\r
4530 \r
4531     case IDM_Rematch:\r
4532       RematchEvent();\r
4533       break;\r
4534 \r
4535     case IDM_CallFlag:\r
4536       CallFlagEvent();\r
4537       break;\r
4538 \r
4539     case IDM_Draw:\r
4540       DrawEvent();\r
4541       break;\r
4542 \r
4543     case IDM_Adjourn:\r
4544       AdjournEvent();\r
4545       break;\r
4546 \r
4547     case IDM_Abort:\r
4548       AbortEvent();\r
4549       break;\r
4550 \r
4551     case IDM_Resign:\r
4552       ResignEvent();\r
4553       break;\r
4554 \r
4555     case IDM_StopObserving:\r
4556       StopObservingEvent();\r
4557       break;\r
4558 \r
4559     case IDM_StopExamining:\r
4560       StopExaminingEvent();\r
4561       break;\r
4562 \r
4563     case IDM_TypeInMove:\r
4564       PopUpMoveDialog('\000');\r
4565       break;\r
4566 \r
4567     case IDM_TypeInName:\r
4568       PopUpNameDialog('\000');\r
4569       break;\r
4570 \r
4571     case IDM_Backward:\r
4572       BackwardEvent();\r
4573       SetFocus(hwndMain);\r
4574       break;\r
4575 \r
4576     JAWS_MENU_ITEMS\r
4577 \r
4578     case IDM_Forward:\r
4579       ForwardEvent();\r
4580       SetFocus(hwndMain);\r
4581       break;\r
4582 \r
4583     case IDM_ToStart:\r
4584       ToStartEvent();\r
4585       SetFocus(hwndMain);\r
4586       break;\r
4587 \r
4588     case IDM_ToEnd:\r
4589       ToEndEvent();\r
4590       SetFocus(hwndMain);\r
4591       break;\r
4592 \r
4593     case IDM_Revert:\r
4594       RevertEvent();\r
4595       break;\r
4596 \r
4597     case IDM_TruncateGame:\r
4598       TruncateGameEvent();\r
4599       break;\r
4600 \r
4601     case IDM_MoveNow:\r
4602       MoveNowEvent();\r
4603       break;\r
4604 \r
4605     case IDM_RetractMove:\r
4606       RetractMoveEvent();\r
4607       break;\r
4608 \r
4609     case IDM_FlipView:\r
4610       flipView = !flipView;\r
4611       DrawPosition(FALSE, NULL);\r
4612       break;\r
4613 \r
4614     case IDM_FlipClock:\r
4615       flipClock = !flipClock;\r
4616       DisplayBothClocks();\r
4617       DrawPosition(FALSE, NULL);\r
4618       break;\r
4619 \r
4620     case IDM_MuteSounds:\r
4621       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4622       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4623                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4624       break;\r
4625 \r
4626     case IDM_GeneralOptions:\r
4627       GeneralOptionsPopup(hwnd);\r
4628       DrawPosition(TRUE, NULL);\r
4629       break;\r
4630 \r
4631     case IDM_BoardOptions:\r
4632       BoardOptionsPopup(hwnd);\r
4633       break;\r
4634 \r
4635     case IDM_EnginePlayOptions:\r
4636       EnginePlayOptionsPopup(hwnd);\r
4637       break;\r
4638 \r
4639     case IDM_Engine1Options:\r
4640       EngineOptionsPopup(hwnd, &first);\r
4641       break;\r
4642 \r
4643     case IDM_Engine2Options:\r
4644       EngineOptionsPopup(hwnd, &second);\r
4645       break;\r
4646 \r
4647     case IDM_OptionsUCI:\r
4648       UciOptionsPopup(hwnd);\r
4649       break;\r
4650 \r
4651     case IDM_IcsOptions:\r
4652       IcsOptionsPopup(hwnd);\r
4653       break;\r
4654 \r
4655     case IDM_Fonts:\r
4656       FontsOptionsPopup(hwnd);\r
4657       break;\r
4658 \r
4659     case IDM_Sounds:\r
4660       SoundOptionsPopup(hwnd);\r
4661       break;\r
4662 \r
4663     case IDM_CommPort:\r
4664       CommPortOptionsPopup(hwnd);\r
4665       break;\r
4666 \r
4667     case IDM_LoadOptions:\r
4668       LoadOptionsPopup(hwnd);\r
4669       break;\r
4670 \r
4671     case IDM_SaveOptions:\r
4672       SaveOptionsPopup(hwnd);\r
4673       break;\r
4674 \r
4675     case IDM_TimeControl:\r
4676       TimeControlOptionsPopup(hwnd);\r
4677       break;\r
4678 \r
4679     case IDM_SaveSettings:\r
4680       SaveSettings(settingsFileName);\r
4681       break;\r
4682 \r
4683     case IDM_SaveSettingsOnExit:\r
4684       saveSettingsOnExit = !saveSettingsOnExit;\r
4685       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4686                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4687                                          MF_CHECKED : MF_UNCHECKED));\r
4688       break;\r
4689 \r
4690     case IDM_Hint:\r
4691       HintEvent();\r
4692       break;\r
4693 \r
4694     case IDM_Book:\r
4695       BookEvent();\r
4696       break;\r
4697 \r
4698     case IDM_AboutGame:\r
4699       AboutGameEvent();\r
4700       break;\r
4701 \r
4702     case IDM_Debug:\r
4703       appData.debugMode = !appData.debugMode;\r
4704       if (appData.debugMode) {\r
4705         char dir[MSG_SIZ];\r
4706         GetCurrentDirectory(MSG_SIZ, dir);\r
4707         SetCurrentDirectory(installDir);\r
4708         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4709         SetCurrentDirectory(dir);\r
4710         setbuf(debugFP, NULL);\r
4711       } else {\r
4712         fclose(debugFP);\r
4713         debugFP = NULL;\r
4714       }\r
4715       break;\r
4716 \r
4717     case IDM_HELPCONTENTS:\r
4718       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4719           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4720           MessageBox (GetFocus(),\r
4721                     "Unable to activate help",\r
4722                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4723       }\r
4724       break;\r
4725 \r
4726     case IDM_HELPSEARCH:\r
4727         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4728             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4729         MessageBox (GetFocus(),\r
4730                     "Unable to activate help",\r
4731                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4732       }\r
4733       break;\r
4734 \r
4735     case IDM_HELPHELP:\r
4736       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4737         MessageBox (GetFocus(),\r
4738                     "Unable to activate help",\r
4739                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4740       }\r
4741       break;\r
4742 \r
4743     case IDM_ABOUT:\r
4744       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4745       DialogBox(hInst, \r
4746         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4747         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4748       FreeProcInstance(lpProc);\r
4749       break;\r
4750 \r
4751     case IDM_DirectCommand1:\r
4752       AskQuestionEvent("Direct Command",\r
4753                        "Send to chess program:", "", "1");\r
4754       break;\r
4755     case IDM_DirectCommand2:\r
4756       AskQuestionEvent("Direct Command",\r
4757                        "Send to second chess program:", "", "2");\r
4758       break;\r
4759 \r
4760     case EP_WhitePawn:\r
4761       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4762       fromX = fromY = -1;\r
4763       break;\r
4764 \r
4765     case EP_WhiteKnight:\r
4766       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4767       fromX = fromY = -1;\r
4768       break;\r
4769 \r
4770     case EP_WhiteBishop:\r
4771       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4772       fromX = fromY = -1;\r
4773       break;\r
4774 \r
4775     case EP_WhiteRook:\r
4776       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4777       fromX = fromY = -1;\r
4778       break;\r
4779 \r
4780     case EP_WhiteQueen:\r
4781       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4782       fromX = fromY = -1;\r
4783       break;\r
4784 \r
4785     case EP_WhiteFerz:\r
4786       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4787       fromX = fromY = -1;\r
4788       break;\r
4789 \r
4790     case EP_WhiteWazir:\r
4791       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4792       fromX = fromY = -1;\r
4793       break;\r
4794 \r
4795     case EP_WhiteAlfil:\r
4796       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4797       fromX = fromY = -1;\r
4798       break;\r
4799 \r
4800     case EP_WhiteCannon:\r
4801       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4802       fromX = fromY = -1;\r
4803       break;\r
4804 \r
4805     case EP_WhiteCardinal:\r
4806       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4807       fromX = fromY = -1;\r
4808       break;\r
4809 \r
4810     case EP_WhiteMarshall:\r
4811       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4812       fromX = fromY = -1;\r
4813       break;\r
4814 \r
4815     case EP_WhiteKing:\r
4816       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4817       fromX = fromY = -1;\r
4818       break;\r
4819 \r
4820     case EP_BlackPawn:\r
4821       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4822       fromX = fromY = -1;\r
4823       break;\r
4824 \r
4825     case EP_BlackKnight:\r
4826       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4827       fromX = fromY = -1;\r
4828       break;\r
4829 \r
4830     case EP_BlackBishop:\r
4831       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4832       fromX = fromY = -1;\r
4833       break;\r
4834 \r
4835     case EP_BlackRook:\r
4836       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4837       fromX = fromY = -1;\r
4838       break;\r
4839 \r
4840     case EP_BlackQueen:\r
4841       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4842       fromX = fromY = -1;\r
4843       break;\r
4844 \r
4845     case EP_BlackFerz:\r
4846       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4847       fromX = fromY = -1;\r
4848       break;\r
4849 \r
4850     case EP_BlackWazir:\r
4851       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
4852       fromX = fromY = -1;\r
4853       break;\r
4854 \r
4855     case EP_BlackAlfil:\r
4856       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
4857       fromX = fromY = -1;\r
4858       break;\r
4859 \r
4860     case EP_BlackCannon:\r
4861       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
4862       fromX = fromY = -1;\r
4863       break;\r
4864 \r
4865     case EP_BlackCardinal:\r
4866       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
4867       fromX = fromY = -1;\r
4868       break;\r
4869 \r
4870     case EP_BlackMarshall:\r
4871       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
4872       fromX = fromY = -1;\r
4873       break;\r
4874 \r
4875     case EP_BlackKing:\r
4876       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
4877       fromX = fromY = -1;\r
4878       break;\r
4879 \r
4880     case EP_EmptySquare:\r
4881       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
4882       fromX = fromY = -1;\r
4883       break;\r
4884 \r
4885     case EP_ClearBoard:\r
4886       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
4887       fromX = fromY = -1;\r
4888       break;\r
4889 \r
4890     case EP_White:\r
4891       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
4892       fromX = fromY = -1;\r
4893       break;\r
4894 \r
4895     case EP_Black:\r
4896       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
4897       fromX = fromY = -1;\r
4898       break;\r
4899 \r
4900     case EP_Promote:\r
4901       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
4902       fromX = fromY = -1;\r
4903       break;\r
4904 \r
4905     case EP_Demote:\r
4906       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
4907       fromX = fromY = -1;\r
4908       break;\r
4909 \r
4910     case DP_Pawn:\r
4911       DropMenuEvent(WhitePawn, fromX, fromY);\r
4912       fromX = fromY = -1;\r
4913       break;\r
4914 \r
4915     case DP_Knight:\r
4916       DropMenuEvent(WhiteKnight, fromX, fromY);\r
4917       fromX = fromY = -1;\r
4918       break;\r
4919 \r
4920     case DP_Bishop:\r
4921       DropMenuEvent(WhiteBishop, fromX, fromY);\r
4922       fromX = fromY = -1;\r
4923       break;\r
4924 \r
4925     case DP_Rook:\r
4926       DropMenuEvent(WhiteRook, fromX, fromY);\r
4927       fromX = fromY = -1;\r
4928       break;\r
4929 \r
4930     case DP_Queen:\r
4931       DropMenuEvent(WhiteQueen, fromX, fromY);\r
4932       fromX = fromY = -1;\r
4933       break;\r
4934 \r
4935     default:\r
4936       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4937     }\r
4938     break;\r
4939 \r
4940   case WM_TIMER:\r
4941     switch (wParam) {\r
4942     case CLOCK_TIMER_ID:\r
4943       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
4944       clockTimerEvent = 0;\r
4945       DecrementClocks(); /* call into back end */\r
4946       break;\r
4947     case LOAD_GAME_TIMER_ID:\r
4948       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
4949       loadGameTimerEvent = 0;\r
4950       AutoPlayGameLoop(); /* call into back end */\r
4951       break;\r
4952     case ANALYSIS_TIMER_ID:\r
4953       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
4954                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
4955         AnalysisPeriodicEvent(0);\r
4956       } else {\r
4957         KillTimer(hwnd, analysisTimerEvent);\r
4958         analysisTimerEvent = 0;\r
4959       }\r
4960       break;\r
4961     case DELAYED_TIMER_ID:\r
4962       KillTimer(hwnd, delayedTimerEvent);\r
4963       delayedTimerEvent = 0;\r
4964       delayedTimerCallback();\r
4965       break;\r
4966     }\r
4967     break;\r
4968 \r
4969   case WM_USER_Input:\r
4970     InputEvent(hwnd, message, wParam, lParam);\r
4971     break;\r
4972 \r
4973   /* [AS] Also move "attached" child windows */\r
4974   case WM_WINDOWPOSCHANGING:\r
4975 \r
4976     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
4977         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
4978 \r
4979         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
4980             /* Window is moving */\r
4981             RECT rcMain;\r
4982 \r
4983 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
4984             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
4985             rcMain.right  = wpMain.x + wpMain.width;\r
4986             rcMain.top    = wpMain.y;\r
4987             rcMain.bottom = wpMain.y + wpMain.height;\r
4988             \r
4989             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
4990             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
4991             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
4992             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
4993             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
4994             wpMain.x = lpwp->x;\r
4995             wpMain.y = lpwp->y;\r
4996         }\r
4997     }\r
4998     break;\r
4999 \r
5000   /* [AS] Snapping */\r
5001   case WM_ENTERSIZEMOVE:\r
5002     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5003     if (hwnd == hwndMain) {\r
5004       doingSizing = TRUE;\r
5005       lastSizing = 0;\r
5006     }\r
5007     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5008     break;\r
5009 \r
5010   case WM_SIZING:\r
5011     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5012     if (hwnd == hwndMain) {\r
5013       lastSizing = wParam;\r
5014     }\r
5015     break;\r
5016 \r
5017   case WM_MOVING:\r
5018     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5019       return OnMoving( &sd, hwnd, wParam, lParam );\r
5020 \r
5021   case WM_EXITSIZEMOVE:\r
5022     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5023     if (hwnd == hwndMain) {\r
5024       RECT client;\r
5025       doingSizing = FALSE;\r
5026       InvalidateRect(hwnd, &boardRect, FALSE);\r
5027       GetClientRect(hwnd, &client);\r
5028       ResizeBoard(client.right, client.bottom, lastSizing);\r
5029       lastSizing = 0;\r
5030       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5031     }\r
5032     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5033     break;\r
5034 \r
5035   case WM_DESTROY: /* message: window being destroyed */\r
5036     PostQuitMessage(0);\r
5037     break;\r
5038 \r
5039   case WM_CLOSE:\r
5040     if (hwnd == hwndMain) {\r
5041       ExitEvent(0);\r
5042     }\r
5043     break;\r
5044 \r
5045   default:      /* Passes it on if unprocessed */\r
5046     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5047   }\r
5048   return 0;\r
5049 }\r
5050 \r
5051 /*---------------------------------------------------------------------------*\\r
5052  *\r
5053  * Misc utility routines\r
5054  *\r
5055 \*---------------------------------------------------------------------------*/\r
5056 \r
5057 /*\r
5058  * Decent random number generator, at least not as bad as Windows\r
5059  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5060  */\r
5061 unsigned int randstate;\r
5062 \r
5063 int\r
5064 myrandom(void)\r
5065 {\r
5066   randstate = randstate * 1664525 + 1013904223;\r
5067   return (int) randstate & 0x7fffffff;\r
5068 }\r
5069 \r
5070 void\r
5071 mysrandom(unsigned int seed)\r
5072 {\r
5073   randstate = seed;\r
5074 }\r
5075 \r
5076 \r
5077 /* \r
5078  * returns TRUE if user selects a different color, FALSE otherwise \r
5079  */\r
5080 \r
5081 BOOL\r
5082 ChangeColor(HWND hwnd, COLORREF *which)\r
5083 {\r
5084   static BOOL firstTime = TRUE;\r
5085   static DWORD customColors[16];\r
5086   CHOOSECOLOR cc;\r
5087   COLORREF newcolor;\r
5088   int i;\r
5089   ColorClass ccl;\r
5090 \r
5091   if (firstTime) {\r
5092     /* Make initial colors in use available as custom colors */\r
5093     /* Should we put the compiled-in defaults here instead? */\r
5094     i = 0;\r
5095     customColors[i++] = lightSquareColor & 0xffffff;\r
5096     customColors[i++] = darkSquareColor & 0xffffff;\r
5097     customColors[i++] = whitePieceColor & 0xffffff;\r
5098     customColors[i++] = blackPieceColor & 0xffffff;\r
5099     customColors[i++] = highlightSquareColor & 0xffffff;\r
5100     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5101 \r
5102     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5103       customColors[i++] = textAttribs[ccl].color;\r
5104     }\r
5105     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5106     firstTime = FALSE;\r
5107   }\r
5108 \r
5109   cc.lStructSize = sizeof(cc);\r
5110   cc.hwndOwner = hwnd;\r
5111   cc.hInstance = NULL;\r
5112   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5113   cc.lpCustColors = (LPDWORD) customColors;\r
5114   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5115 \r
5116   if (!ChooseColor(&cc)) return FALSE;\r
5117 \r
5118   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5119   if (newcolor == *which) return FALSE;\r
5120   *which = newcolor;\r
5121   return TRUE;\r
5122 \r
5123   /*\r
5124   InitDrawingColors();\r
5125   InvalidateRect(hwnd, &boardRect, FALSE);\r
5126   */\r
5127 }\r
5128 \r
5129 BOOLEAN\r
5130 MyLoadSound(MySound *ms)\r
5131 {\r
5132   BOOL ok = FALSE;\r
5133   struct stat st;\r
5134   FILE *f;\r
5135 \r
5136   if (ms->data) free(ms->data);\r
5137   ms->data = NULL;\r
5138 \r
5139   switch (ms->name[0]) {\r
5140   case NULLCHAR:\r
5141     /* Silence */\r
5142     ok = TRUE;\r
5143     break;\r
5144   case '$':\r
5145     /* System sound from Control Panel.  Don't preload here. */\r
5146     ok = TRUE;\r
5147     break;\r
5148   case '!':\r
5149     if (ms->name[1] == NULLCHAR) {\r
5150       /* "!" alone = silence */\r
5151       ok = TRUE;\r
5152     } else {\r
5153       /* Builtin wave resource.  Error if not found. */\r
5154       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5155       if (h == NULL) break;\r
5156       ms->data = (void *)LoadResource(hInst, h);\r
5157       if (h == NULL) break;\r
5158       ok = TRUE;\r
5159     }\r
5160     break;\r
5161   default:\r
5162     /* .wav file.  Error if not found. */\r
5163     f = fopen(ms->name, "rb");\r
5164     if (f == NULL) break;\r
5165     if (fstat(fileno(f), &st) < 0) break;\r
5166     ms->data = malloc(st.st_size);\r
5167     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5168     fclose(f);\r
5169     ok = TRUE;\r
5170     break;\r
5171   }\r
5172   if (!ok) {\r
5173     char buf[MSG_SIZ];\r
5174     sprintf(buf, "Error loading sound %s", ms->name);\r
5175     DisplayError(buf, GetLastError());\r
5176   }\r
5177   return ok;\r
5178 }\r
5179 \r
5180 BOOLEAN\r
5181 MyPlaySound(MySound *ms)\r
5182 {\r
5183   BOOLEAN ok = FALSE;\r
5184 \r
5185   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5186   switch (ms->name[0]) {\r
5187   case NULLCHAR:\r
5188         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5189     /* Silence */\r
5190     ok = TRUE;\r
5191     break;\r
5192   case '$':\r
5193     /* System sound from Control Panel (deprecated feature).\r
5194        "$" alone or an unset sound name gets default beep (still in use). */\r
5195     if (ms->name[1]) {\r
5196       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5197     }\r
5198     if (!ok) ok = MessageBeep(MB_OK);\r
5199     break; \r
5200   case '!':\r
5201     /* Builtin wave resource, or "!" alone for silence */\r
5202     if (ms->name[1]) {\r
5203       if (ms->data == NULL) return FALSE;\r
5204       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5205     } else {\r
5206       ok = TRUE;\r
5207     }\r
5208     break;\r
5209   default:\r
5210     /* .wav file.  Error if not found. */\r
5211     if (ms->data == NULL) return FALSE;\r
5212     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5213     break;\r
5214   }\r
5215   /* Don't print an error: this can happen innocently if the sound driver\r
5216      is busy; for instance, if another instance of WinBoard is playing\r
5217      a sound at about the same time. */\r
5218   return ok;\r
5219 }\r
5220 \r
5221 \r
5222 LRESULT CALLBACK\r
5223 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5224 {\r
5225   BOOL ok;\r
5226   OPENFILENAME *ofn;\r
5227   static UINT *number; /* gross that this is static */\r
5228 \r
5229   switch (message) {\r
5230   case WM_INITDIALOG: /* message: initialize dialog box */\r
5231     /* Center the dialog over the application window */\r
5232     ofn = (OPENFILENAME *) lParam;\r
5233     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5234       number = (UINT *) ofn->lCustData;\r
5235       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5236     } else {\r
5237       number = NULL;\r
5238     }\r
5239     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5240     return FALSE;  /* Allow for further processing */\r
5241 \r
5242   case WM_COMMAND:\r
5243     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5244       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5245     }\r
5246     return FALSE;  /* Allow for further processing */\r
5247   }\r
5248   return FALSE;\r
5249 }\r
5250 \r
5251 UINT APIENTRY\r
5252 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5253 {\r
5254   static UINT *number;\r
5255   OPENFILENAME *ofname;\r
5256   OFNOTIFY *ofnot;\r
5257   switch (uiMsg) {\r
5258   case WM_INITDIALOG:\r
5259     ofname = (OPENFILENAME *)lParam;\r
5260     number = (UINT *)(ofname->lCustData);\r
5261     break;\r
5262   case WM_NOTIFY:\r
5263     ofnot = (OFNOTIFY *)lParam;\r
5264     if (ofnot->hdr.code == CDN_FILEOK) {\r
5265       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5266     }\r
5267     break;\r
5268   }\r
5269   return 0;\r
5270 }\r
5271 \r
5272 \r
5273 FILE *\r
5274 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5275                char *nameFilt, char *dlgTitle, UINT *number,\r
5276                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5277 {\r
5278   OPENFILENAME openFileName;\r
5279   char buf1[MSG_SIZ];\r
5280   FILE *f;\r
5281 \r
5282   if (fileName == NULL) fileName = buf1;\r
5283   if (defName == NULL) {\r
5284     strcpy(fileName, "*.");\r
5285     strcat(fileName, defExt);\r
5286   } else {\r
5287     strcpy(fileName, defName);\r
5288   }\r
5289   if (fileTitle) strcpy(fileTitle, "");\r
5290   if (number) *number = 0;\r
5291 \r
5292   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5293   openFileName.hwndOwner         = hwnd;\r
5294   openFileName.hInstance         = (HANDLE) hInst;\r
5295   openFileName.lpstrFilter       = nameFilt;\r
5296   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5297   openFileName.nMaxCustFilter    = 0L;\r
5298   openFileName.nFilterIndex      = 1L;\r
5299   openFileName.lpstrFile         = fileName;\r
5300   openFileName.nMaxFile          = MSG_SIZ;\r
5301   openFileName.lpstrFileTitle    = fileTitle;\r
5302   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5303   openFileName.lpstrInitialDir   = NULL;\r
5304   openFileName.lpstrTitle        = dlgTitle;\r
5305   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5306     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5307     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5308     | (oldDialog ? 0 : OFN_EXPLORER);\r
5309   openFileName.nFileOffset       = 0;\r
5310   openFileName.nFileExtension    = 0;\r
5311   openFileName.lpstrDefExt       = defExt;\r
5312   openFileName.lCustData         = (LONG) number;\r
5313   openFileName.lpfnHook          = oldDialog ?\r
5314     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5315   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5316 \r
5317   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5318                         GetOpenFileName(&openFileName)) {\r
5319     /* open the file */\r
5320     f = fopen(openFileName.lpstrFile, write);\r
5321     if (f == NULL) {\r
5322       MessageBox(hwnd, "File open failed", NULL,\r
5323                  MB_OK|MB_ICONEXCLAMATION);\r
5324       return NULL;\r
5325     }\r
5326   } else {\r
5327     int err = CommDlgExtendedError();\r
5328     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5329     return FALSE;\r
5330   }\r
5331   return f;\r
5332 }\r
5333 \r
5334 \r
5335 \r
5336 VOID APIENTRY\r
5337 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5338 {\r
5339   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5340 \r
5341   /*\r
5342    * Get the first pop-up menu in the menu template. This is the\r
5343    * menu that TrackPopupMenu displays.\r
5344    */\r
5345   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5346 \r
5347   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5348 \r
5349   /*\r
5350    * TrackPopup uses screen coordinates, so convert the\r
5351    * coordinates of the mouse click to screen coordinates.\r
5352    */\r
5353   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5354 \r
5355   /* Draw and track the floating pop-up menu. */\r
5356   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5357                  pt.x, pt.y, 0, hwnd, NULL);\r
5358 \r
5359   /* Destroy the menu.*/\r
5360   DestroyMenu(hmenu);\r
5361 }\r
5362    \r
5363 typedef struct {\r
5364   HWND hDlg, hText;\r
5365   int sizeX, sizeY, newSizeX, newSizeY;\r
5366   HDWP hdwp;\r
5367 } ResizeEditPlusButtonsClosure;\r
5368 \r
5369 BOOL CALLBACK\r
5370 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5371 {\r
5372   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5373   RECT rect;\r
5374   POINT pt;\r
5375 \r
5376   if (hChild == cl->hText) return TRUE;\r
5377   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5378   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5379   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5380   ScreenToClient(cl->hDlg, &pt);\r
5381   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5382     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5383   return TRUE;\r
5384 }\r
5385 \r
5386 /* Resize a dialog that has a (rich) edit field filling most of\r
5387    the top, with a row of buttons below */\r
5388 VOID\r
5389 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5390 {\r
5391   RECT rectText;\r
5392   int newTextHeight, newTextWidth;\r
5393   ResizeEditPlusButtonsClosure cl;\r
5394   \r
5395   /*if (IsIconic(hDlg)) return;*/\r
5396   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5397   \r
5398   cl.hdwp = BeginDeferWindowPos(8);\r
5399 \r
5400   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5401   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5402   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5403   if (newTextHeight < 0) {\r
5404     newSizeY += -newTextHeight;\r
5405     newTextHeight = 0;\r
5406   }\r
5407   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5408     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5409 \r
5410   cl.hDlg = hDlg;\r
5411   cl.hText = hText;\r
5412   cl.sizeX = sizeX;\r
5413   cl.sizeY = sizeY;\r
5414   cl.newSizeX = newSizeX;\r
5415   cl.newSizeY = newSizeY;\r
5416   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5417 \r
5418   EndDeferWindowPos(cl.hdwp);\r
5419 }\r
5420 \r
5421 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5422 {\r
5423     RECT    rChild, rParent;\r
5424     int     wChild, hChild, wParent, hParent;\r
5425     int     wScreen, hScreen, xNew, yNew;\r
5426     HDC     hdc;\r
5427 \r
5428     /* Get the Height and Width of the child window */\r
5429     GetWindowRect (hwndChild, &rChild);\r
5430     wChild = rChild.right - rChild.left;\r
5431     hChild = rChild.bottom - rChild.top;\r
5432 \r
5433     /* Get the Height and Width of the parent window */\r
5434     GetWindowRect (hwndParent, &rParent);\r
5435     wParent = rParent.right - rParent.left;\r
5436     hParent = rParent.bottom - rParent.top;\r
5437 \r
5438     /* Get the display limits */\r
5439     hdc = GetDC (hwndChild);\r
5440     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5441     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5442     ReleaseDC(hwndChild, hdc);\r
5443 \r
5444     /* Calculate new X position, then adjust for screen */\r
5445     xNew = rParent.left + ((wParent - wChild) /2);\r
5446     if (xNew < 0) {\r
5447         xNew = 0;\r
5448     } else if ((xNew+wChild) > wScreen) {\r
5449         xNew = wScreen - wChild;\r
5450     }\r
5451 \r
5452     /* Calculate new Y position, then adjust for screen */\r
5453     if( mode == 0 ) {\r
5454         yNew = rParent.top  + ((hParent - hChild) /2);\r
5455     }\r
5456     else {\r
5457         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5458     }\r
5459 \r
5460     if (yNew < 0) {\r
5461         yNew = 0;\r
5462     } else if ((yNew+hChild) > hScreen) {\r
5463         yNew = hScreen - hChild;\r
5464     }\r
5465 \r
5466     /* Set it, and return */\r
5467     return SetWindowPos (hwndChild, NULL,\r
5468                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5469 }\r
5470 \r
5471 /* Center one window over another */\r
5472 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5473 {\r
5474     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5475 }\r
5476 \r
5477 /*---------------------------------------------------------------------------*\\r
5478  *\r
5479  * Startup Dialog functions\r
5480  *\r
5481 \*---------------------------------------------------------------------------*/\r
5482 void\r
5483 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5484 {\r
5485   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5486 \r
5487   while (*cd != NULL) {\r
5488     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5489     cd++;\r
5490   }\r
5491 }\r
5492 \r
5493 void\r
5494 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5495 {\r
5496   char buf1[MAX_ARG_LEN];\r
5497   int len;\r
5498 \r
5499   if (str[0] == '@') {\r
5500     FILE* f = fopen(str + 1, "r");\r
5501     if (f == NULL) {\r
5502       DisplayFatalError(str + 1, errno, 2);\r
5503       return;\r
5504     }\r
5505     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5506     fclose(f);\r
5507     buf1[len] = NULLCHAR;\r
5508     str = buf1;\r
5509   }\r
5510 \r
5511   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5512 \r
5513   for (;;) {\r
5514     char buf[MSG_SIZ];\r
5515     char *end = strchr(str, '\n');\r
5516     if (end == NULL) return;\r
5517     memcpy(buf, str, end - str);\r
5518     buf[end - str] = NULLCHAR;\r
5519     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5520     str = end + 1;\r
5521   }\r
5522 }\r
5523 \r
5524 void\r
5525 SetStartupDialogEnables(HWND hDlg)\r
5526 {\r
5527   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5528     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5529     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5530   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5531     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5532   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5533     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5534   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5535     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5536   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5537     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5538     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5539     IsDlgButtonChecked(hDlg, OPT_View));\r
5540 }\r
5541 \r
5542 char *\r
5543 QuoteForFilename(char *filename)\r
5544 {\r
5545   int dquote, space;\r
5546   dquote = strchr(filename, '"') != NULL;\r
5547   space = strchr(filename, ' ') != NULL;\r
5548   if (dquote || space) {\r
5549     if (dquote) {\r
5550       return "'";\r
5551     } else {\r
5552       return "\"";\r
5553     }\r
5554   } else {\r
5555     return "";\r
5556   }\r
5557 }\r
5558 \r
5559 VOID\r
5560 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5561 {\r
5562   char buf[MSG_SIZ];\r
5563   char *q;\r
5564 \r
5565   InitComboStringsFromOption(hwndCombo, nthnames);\r
5566   q = QuoteForFilename(nthcp);\r
5567   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5568   if (*nthdir != NULLCHAR) {\r
5569     q = QuoteForFilename(nthdir);\r
5570     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5571   }\r
5572   if (*nthcp == NULLCHAR) {\r
5573     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5574   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5575     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5576     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5577   }\r
5578 }\r
5579 \r
5580 LRESULT CALLBACK\r
5581 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5582 {\r
5583   char buf[MSG_SIZ];\r
5584   HANDLE hwndCombo;\r
5585   char *p;\r
5586 \r
5587   switch (message) {\r
5588   case WM_INITDIALOG:\r
5589     /* Center the dialog */\r
5590     CenterWindow (hDlg, GetDesktopWindow());\r
5591     /* Initialize the dialog items */\r
5592     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5593                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5594                   firstChessProgramNames);\r
5595     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5596                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5597                   secondChessProgramNames);\r
5598     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5599     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5600     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5601     if (*appData.icsHelper != NULLCHAR) {\r
5602       char *q = QuoteForFilename(appData.icsHelper);\r
5603       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5604     }\r
5605     if (*appData.icsHost == NULLCHAR) {\r
5606       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5607       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5608     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5609       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5610       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5611     }\r
5612 \r
5613     if (appData.icsActive) {\r
5614       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5615     }\r
5616     else if (appData.noChessProgram) {\r
5617       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5618     }\r
5619     else {\r
5620       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5621     }\r
5622 \r
5623     SetStartupDialogEnables(hDlg);\r
5624     return TRUE;\r
5625 \r
5626   case WM_COMMAND:\r
5627     switch (LOWORD(wParam)) {\r
5628     case IDOK:\r
5629       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5630         strcpy(buf, "/fcp=");\r
5631         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5632         p = buf;\r
5633         ParseArgs(StringGet, &p);\r
5634         strcpy(buf, "/scp=");\r
5635         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5636         p = buf;\r
5637         ParseArgs(StringGet, &p);\r
5638         appData.noChessProgram = FALSE;\r
5639         appData.icsActive = FALSE;\r
5640       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5641         strcpy(buf, "/ics /icshost=");\r
5642         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5643         p = buf;\r
5644         ParseArgs(StringGet, &p);\r
5645         if (appData.zippyPlay) {\r
5646           strcpy(buf, "/fcp=");\r
5647           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5648           p = buf;\r
5649           ParseArgs(StringGet, &p);\r
5650         }\r
5651       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5652         appData.noChessProgram = TRUE;\r
5653         appData.icsActive = FALSE;\r
5654       } else {\r
5655         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5656                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5657         return TRUE;\r
5658       }\r
5659       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5660         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5661         p = buf;\r
5662         ParseArgs(StringGet, &p);\r
5663       }\r
5664       EndDialog(hDlg, TRUE);\r
5665       return TRUE;\r
5666 \r
5667     case IDCANCEL:\r
5668       ExitEvent(0);\r
5669       return TRUE;\r
5670 \r
5671     case IDM_HELPCONTENTS:\r
5672       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5673         MessageBox (GetFocus(),\r
5674                     "Unable to activate help",\r
5675                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5676       }\r
5677       break;\r
5678 \r
5679     default:\r
5680       SetStartupDialogEnables(hDlg);\r
5681       break;\r
5682     }\r
5683     break;\r
5684   }\r
5685   return FALSE;\r
5686 }\r
5687 \r
5688 /*---------------------------------------------------------------------------*\\r
5689  *\r
5690  * About box dialog functions\r
5691  *\r
5692 \*---------------------------------------------------------------------------*/\r
5693 \r
5694 /* Process messages for "About" dialog box */\r
5695 LRESULT CALLBACK\r
5696 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5697 {\r
5698   switch (message) {\r
5699   case WM_INITDIALOG: /* message: initialize dialog box */\r
5700     /* Center the dialog over the application window */\r
5701     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5702     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5703     JAWS_COPYRIGHT\r
5704     return (TRUE);\r
5705 \r
5706   case WM_COMMAND: /* message: received a command */\r
5707     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5708         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5709       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5710       return (TRUE);\r
5711     }\r
5712     break;\r
5713   }\r
5714   return (FALSE);\r
5715 }\r
5716 \r
5717 /*---------------------------------------------------------------------------*\\r
5718  *\r
5719  * Comment Dialog functions\r
5720  *\r
5721 \*---------------------------------------------------------------------------*/\r
5722 \r
5723 LRESULT CALLBACK\r
5724 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5725 {\r
5726   static HANDLE hwndText = NULL;\r
5727   int len, newSizeX, newSizeY, flags;\r
5728   static int sizeX, sizeY;\r
5729   char *str;\r
5730   RECT rect;\r
5731   MINMAXINFO *mmi;\r
5732 \r
5733   switch (message) {\r
5734   case WM_INITDIALOG: /* message: initialize dialog box */\r
5735     /* Initialize the dialog items */\r
5736     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5737     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5738     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5739     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5740     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5741     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5742     SetWindowText(hDlg, commentTitle);\r
5743     if (editComment) {\r
5744       SetFocus(hwndText);\r
5745     } else {\r
5746       SetFocus(GetDlgItem(hDlg, IDOK));\r
5747     }\r
5748     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5749                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5750                 MAKELPARAM(FALSE, 0));\r
5751     /* Size and position the dialog */\r
5752     if (!commentDialog) {\r
5753       commentDialog = hDlg;\r
5754       flags = SWP_NOZORDER;\r
5755       GetClientRect(hDlg, &rect);\r
5756       sizeX = rect.right;\r
5757       sizeY = rect.bottom;\r
5758       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5759           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5760         WINDOWPLACEMENT wp;\r
5761         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5762         wp.length = sizeof(WINDOWPLACEMENT);\r
5763         wp.flags = 0;\r
5764         wp.showCmd = SW_SHOW;\r
5765         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5766         wp.rcNormalPosition.left = wpComment.x;\r
5767         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5768         wp.rcNormalPosition.top = wpComment.y;\r
5769         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5770         SetWindowPlacement(hDlg, &wp);\r
5771 \r
5772         GetClientRect(hDlg, &rect);\r
5773         newSizeX = rect.right;\r
5774         newSizeY = rect.bottom;\r
5775         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5776                               newSizeX, newSizeY);\r
5777         sizeX = newSizeX;\r
5778         sizeY = newSizeY;\r
5779       }\r
5780     }\r
5781     return FALSE;\r
5782 \r
5783   case WM_COMMAND: /* message: received a command */\r
5784     switch (LOWORD(wParam)) {\r
5785     case IDOK:\r
5786       if (editComment) {\r
5787         char *p, *q;\r
5788         /* Read changed options from the dialog box */\r
5789         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5790         len = GetWindowTextLength(hwndText);\r
5791         str = (char *) malloc(len + 1);\r
5792         GetWindowText(hwndText, str, len + 1);\r
5793         p = q = str;\r
5794         while (*q) {\r
5795           if (*q == '\r')\r
5796             q++;\r
5797           else\r
5798             *p++ = *q++;\r
5799         }\r
5800         *p = NULLCHAR;\r
5801         ReplaceComment(commentIndex, str);\r
5802         free(str);\r
5803       }\r
5804       CommentPopDown();\r
5805       return TRUE;\r
5806 \r
5807     case IDCANCEL:\r
5808     case OPT_CancelComment:\r
5809       CommentPopDown();\r
5810       return TRUE;\r
5811 \r
5812     case OPT_ClearComment:\r
5813       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5814       break;\r
5815 \r
5816     case OPT_EditComment:\r
5817       EditCommentEvent();\r
5818       return TRUE;\r
5819 \r
5820     default:\r
5821       break;\r
5822     }\r
5823     break;\r
5824 \r
5825   case WM_SIZE:\r
5826     newSizeX = LOWORD(lParam);\r
5827     newSizeY = HIWORD(lParam);\r
5828     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
5829     sizeX = newSizeX;\r
5830     sizeY = newSizeY;\r
5831     break;\r
5832 \r
5833   case WM_GETMINMAXINFO:\r
5834     /* Prevent resizing window too small */\r
5835     mmi = (MINMAXINFO *) lParam;\r
5836     mmi->ptMinTrackSize.x = 100;\r
5837     mmi->ptMinTrackSize.y = 100;\r
5838     break;\r
5839   }\r
5840   return FALSE;\r
5841 }\r
5842 \r
5843 VOID\r
5844 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
5845 {\r
5846   FARPROC lpProc;\r
5847   char *p, *q;\r
5848 \r
5849   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
5850 \r
5851   if (str == NULL) str = "";\r
5852   p = (char *) malloc(2 * strlen(str) + 2);\r
5853   q = p;\r
5854   while (*str) {\r
5855     if (*str == '\n') *q++ = '\r';\r
5856     *q++ = *str++;\r
5857   }\r
5858   *q = NULLCHAR;\r
5859   if (commentText != NULL) free(commentText);\r
5860 \r
5861   commentIndex = index;\r
5862   commentTitle = title;\r
5863   commentText = p;\r
5864   editComment = edit;\r
5865 \r
5866   if (commentDialog) {\r
5867     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
5868     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
5869   } else {\r
5870     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
5871     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
5872                  hwndMain, (DLGPROC)lpProc);\r
5873     FreeProcInstance(lpProc);\r
5874   }\r
5875   commentUp = TRUE;\r
5876 }\r
5877 \r
5878 \r
5879 /*---------------------------------------------------------------------------*\\r
5880  *\r
5881  * Type-in move dialog functions\r
5882  * \r
5883 \*---------------------------------------------------------------------------*/\r
5884 \r
5885 LRESULT CALLBACK\r
5886 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5887 {\r
5888   char move[MSG_SIZ];\r
5889   HWND hInput;\r
5890   ChessMove moveType;\r
5891   int fromX, fromY, toX, toY;\r
5892   char promoChar;\r
5893 \r
5894   switch (message) {\r
5895   case WM_INITDIALOG:\r
5896     move[0] = (char) lParam;\r
5897     move[1] = NULLCHAR;\r
5898     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5899     hInput = GetDlgItem(hDlg, OPT_Move);\r
5900     SetWindowText(hInput, move);\r
5901     SetFocus(hInput);\r
5902     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5903     return FALSE;\r
5904 \r
5905   case WM_COMMAND:\r
5906     switch (LOWORD(wParam)) {\r
5907     case IDOK:\r
5908       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
5909       { int n; Board board;\r
5910         // [HGM] FENedit\r
5911         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
5912                 EditPositionPasteFEN(move);\r
5913                 EndDialog(hDlg, TRUE);\r
5914                 return TRUE;\r
5915         }\r
5916         // [HGM] movenum: allow move number to be typed in any mode\r
5917         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
5918           ToNrEvent(2*n-1);\r
5919           EndDialog(hDlg, TRUE);\r
5920           return TRUE;\r
5921         }\r
5922       }\r
5923       if (gameMode != EditGame && currentMove != forwardMostMove && \r
5924         gameMode != Training) {\r
5925         DisplayMoveError("Displayed move is not current");\r
5926       } else {\r
5927 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
5928         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5929           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
5930         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
5931         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5932           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
5933           if (gameMode != Training)\r
5934               forwardMostMove = currentMove;\r
5935           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
5936         } else {\r
5937           DisplayMoveError("Could not parse move");\r
5938         }\r
5939       }\r
5940       EndDialog(hDlg, TRUE);\r
5941       return TRUE;\r
5942     case IDCANCEL:\r
5943       EndDialog(hDlg, FALSE);\r
5944       return TRUE;\r
5945     default:\r
5946       break;\r
5947     }\r
5948     break;\r
5949   }\r
5950   return FALSE;\r
5951 }\r
5952 \r
5953 VOID\r
5954 PopUpMoveDialog(char firstchar)\r
5955 {\r
5956     FARPROC lpProc;\r
5957     \r
5958     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
5959         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
5960         gameMode == AnalyzeMode || gameMode == EditGame || \r
5961         gameMode == EditPosition || gameMode == IcsExamining ||\r
5962         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
5963         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
5964                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
5965                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
5966         gameMode == Training) {\r
5967       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
5968       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
5969         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
5970       FreeProcInstance(lpProc);\r
5971     }\r
5972 }\r
5973 \r
5974 /*---------------------------------------------------------------------------*\\r
5975  *\r
5976  * Type-in name dialog functions\r
5977  * \r
5978 \*---------------------------------------------------------------------------*/\r
5979 \r
5980 LRESULT CALLBACK\r
5981 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5982 {\r
5983   char move[MSG_SIZ];\r
5984   HWND hInput;\r
5985 \r
5986   switch (message) {\r
5987   case WM_INITDIALOG:\r
5988     move[0] = (char) lParam;\r
5989     move[1] = NULLCHAR;\r
5990     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5991     hInput = GetDlgItem(hDlg, OPT_Name);\r
5992     SetWindowText(hInput, move);\r
5993     SetFocus(hInput);\r
5994     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5995     return FALSE;\r
5996 \r
5997   case WM_COMMAND:\r
5998     switch (LOWORD(wParam)) {\r
5999     case IDOK:\r
6000       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6001       appData.userName = strdup(move);\r
6002       SetUserLogo();\r
6003 \r
6004       EndDialog(hDlg, TRUE);\r
6005       return TRUE;\r
6006     case IDCANCEL:\r
6007       EndDialog(hDlg, FALSE);\r
6008       return TRUE;\r
6009     default:\r
6010       break;\r
6011     }\r
6012     break;\r
6013   }\r
6014   return FALSE;\r
6015 }\r
6016 \r
6017 VOID\r
6018 PopUpNameDialog(char firstchar)\r
6019 {\r
6020     FARPROC lpProc;\r
6021     \r
6022       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6023       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6024         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6025       FreeProcInstance(lpProc);\r
6026 }\r
6027 \r
6028 /*---------------------------------------------------------------------------*\\r
6029  *\r
6030  *  Error dialogs\r
6031  * \r
6032 \*---------------------------------------------------------------------------*/\r
6033 \r
6034 /* Nonmodal error box */\r
6035 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6036                              WPARAM wParam, LPARAM lParam);\r
6037 \r
6038 VOID\r
6039 ErrorPopUp(char *title, char *content)\r
6040 {\r
6041   FARPROC lpProc;\r
6042   char *p, *q;\r
6043   BOOLEAN modal = hwndMain == NULL;\r
6044 \r
6045   p = content;\r
6046   q = errorMessage;\r
6047   while (*p) {\r
6048     if (*p == '\n') {\r
6049       if (modal) {\r
6050         *q++ = ' ';\r
6051         p++;\r
6052       } else {\r
6053         *q++ = '\r';\r
6054         *q++ = *p++;\r
6055       }\r
6056     } else {\r
6057       *q++ = *p++;\r
6058     }\r
6059   }\r
6060   *q = NULLCHAR;\r
6061   strncpy(errorTitle, title, sizeof(errorTitle));\r
6062   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6063   \r
6064   if (modal) {\r
6065     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6066   } else {\r
6067     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6068     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6069                  hwndMain, (DLGPROC)lpProc);\r
6070     FreeProcInstance(lpProc);\r
6071   }\r
6072 }\r
6073 \r
6074 VOID\r
6075 ErrorPopDown()\r
6076 {\r
6077   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6078   if (errorDialog == NULL) return;\r
6079   DestroyWindow(errorDialog);\r
6080   errorDialog = NULL;\r
6081   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6082 }\r
6083 \r
6084 LRESULT CALLBACK\r
6085 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6086 {\r
6087   HANDLE hwndText;\r
6088   RECT rChild;\r
6089 \r
6090   switch (message) {\r
6091   case WM_INITDIALOG:\r
6092     GetWindowRect(hDlg, &rChild);\r
6093 \r
6094     /*\r
6095     SetWindowPos(hDlg, NULL, rChild.left,\r
6096       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6097       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6098     */\r
6099 \r
6100     /* \r
6101         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6102         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6103         and it doesn't work when you resize the dialog.\r
6104         For now, just give it a default position.\r
6105     */\r
6106     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6107 \r
6108     errorDialog = hDlg;\r
6109     SetWindowText(hDlg, errorTitle);\r
6110     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6111     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6112     return FALSE;\r
6113 \r
6114   case WM_COMMAND:\r
6115     switch (LOWORD(wParam)) {\r
6116     case IDOK:\r
6117     case IDCANCEL:\r
6118       if (errorDialog == hDlg) errorDialog = NULL;\r
6119       DestroyWindow(hDlg);\r
6120       return TRUE;\r
6121 \r
6122     default:\r
6123       break;\r
6124     }\r
6125     break;\r
6126   }\r
6127   return FALSE;\r
6128 }\r
6129 \r
6130 #ifdef GOTHIC\r
6131 HWND gothicDialog = NULL;\r
6132 \r
6133 LRESULT CALLBACK\r
6134 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6135 {\r
6136   HANDLE hwndText;\r
6137   RECT rChild;\r
6138   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6139 \r
6140   switch (message) {\r
6141   case WM_INITDIALOG:\r
6142     GetWindowRect(hDlg, &rChild);\r
6143 \r
6144     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6145                                                              SWP_NOZORDER);\r
6146 \r
6147     /* \r
6148         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6149         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6150         and it doesn't work when you resize the dialog.\r
6151         For now, just give it a default position.\r
6152     */\r
6153     gothicDialog = hDlg;\r
6154     SetWindowText(hDlg, errorTitle);\r
6155     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6156     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6157     return FALSE;\r
6158 \r
6159   case WM_COMMAND:\r
6160     switch (LOWORD(wParam)) {\r
6161     case IDOK:\r
6162     case IDCANCEL:\r
6163       if (errorDialog == hDlg) errorDialog = NULL;\r
6164       DestroyWindow(hDlg);\r
6165       return TRUE;\r
6166 \r
6167     default:\r
6168       break;\r
6169     }\r
6170     break;\r
6171   }\r
6172   return FALSE;\r
6173 }\r
6174 \r
6175 VOID\r
6176 GothicPopUp(char *title, VariantClass variant)\r
6177 {\r
6178   FARPROC lpProc;\r
6179   static char *lastTitle;\r
6180 \r
6181   strncpy(errorTitle, title, sizeof(errorTitle));\r
6182   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6183 \r
6184   if(lastTitle != title && gothicDialog != NULL) {\r
6185     DestroyWindow(gothicDialog);\r
6186     gothicDialog = NULL;\r
6187   }\r
6188   if(variant != VariantNormal && gothicDialog == NULL) {\r
6189     title = lastTitle;\r
6190     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6191     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6192                  hwndMain, (DLGPROC)lpProc);\r
6193     FreeProcInstance(lpProc);\r
6194   }\r
6195 }\r
6196 #endif\r
6197 \r
6198 /*---------------------------------------------------------------------------*\\r
6199  *\r
6200  *  Ics Interaction console functions\r
6201  *\r
6202 \*---------------------------------------------------------------------------*/\r
6203 \r
6204 #define HISTORY_SIZE 64\r
6205 static char *history[HISTORY_SIZE];\r
6206 int histIn = 0, histP = 0;\r
6207 \r
6208 VOID\r
6209 SaveInHistory(char *cmd)\r
6210 {\r
6211   if (history[histIn] != NULL) {\r
6212     free(history[histIn]);\r
6213     history[histIn] = NULL;\r
6214   }\r
6215   if (*cmd == NULLCHAR) return;\r
6216   history[histIn] = StrSave(cmd);\r
6217   histIn = (histIn + 1) % HISTORY_SIZE;\r
6218   if (history[histIn] != NULL) {\r
6219     free(history[histIn]);\r
6220     history[histIn] = NULL;\r
6221   }\r
6222   histP = histIn;\r
6223 }\r
6224 \r
6225 char *\r
6226 PrevInHistory(char *cmd)\r
6227 {\r
6228   int newhp;\r
6229   if (histP == histIn) {\r
6230     if (history[histIn] != NULL) free(history[histIn]);\r
6231     history[histIn] = StrSave(cmd);\r
6232   }\r
6233   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6234   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6235   histP = newhp;\r
6236   return history[histP];\r
6237 }\r
6238 \r
6239 char *\r
6240 NextInHistory()\r
6241 {\r
6242   if (histP == histIn) return NULL;\r
6243   histP = (histP + 1) % HISTORY_SIZE;\r
6244   return history[histP];   \r
6245 }\r
6246 \r
6247 HMENU\r
6248 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6249 {\r
6250   HMENU hmenu, h;\r
6251   int i = 0;\r
6252   hmenu = LoadMenu(hInst, "TextMenu");\r
6253   h = GetSubMenu(hmenu, 0);\r
6254   while (e->item) {\r
6255     if (strcmp(e->item, "-") == 0) {\r
6256       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6257     } else {\r
6258       if (e->item[0] == '|') {\r
6259         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6260                    IDM_CommandX + i, &e->item[1]);\r
6261       } else {\r
6262         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6263       }\r
6264     }\r
6265     e++;\r
6266     i++;\r
6267   } \r
6268   return hmenu;\r
6269 }\r
6270 \r
6271 WNDPROC consoleTextWindowProc;\r
6272 \r
6273 void\r
6274 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6275 {\r
6276   char buf[MSG_SIZ], name[MSG_SIZ];\r
6277   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6278   CHARRANGE sel;\r
6279 \r
6280   if (!getname) {\r
6281     SetWindowText(hInput, command);\r
6282     if (immediate) {\r
6283       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6284     } else {\r
6285       sel.cpMin = 999999;\r
6286       sel.cpMax = 999999;\r
6287       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6288       SetFocus(hInput);\r
6289     }\r
6290     return;\r
6291   }    \r
6292   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6293   if (sel.cpMin == sel.cpMax) {\r
6294     /* Expand to surrounding word */\r
6295     TEXTRANGE tr;\r
6296     do {\r
6297       tr.chrg.cpMax = sel.cpMin;\r
6298       tr.chrg.cpMin = --sel.cpMin;\r
6299       if (sel.cpMin < 0) break;\r
6300       tr.lpstrText = name;\r
6301       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6302     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6303     sel.cpMin++;\r
6304 \r
6305     do {\r
6306       tr.chrg.cpMin = sel.cpMax;\r
6307       tr.chrg.cpMax = ++sel.cpMax;\r
6308       tr.lpstrText = name;\r
6309       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6310     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6311     sel.cpMax--;\r
6312 \r
6313     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6314       MessageBeep(MB_ICONEXCLAMATION);\r
6315       return;\r
6316     }\r
6317     tr.chrg = sel;\r
6318     tr.lpstrText = name;\r
6319     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6320   } else {\r
6321     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6322       MessageBeep(MB_ICONEXCLAMATION);\r
6323       return;\r
6324     }\r
6325     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6326   }\r
6327   if (immediate) {\r
6328     sprintf(buf, "%s %s", command, name);\r
6329     SetWindowText(hInput, buf);\r
6330     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6331   } else {\r
6332     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6333     SetWindowText(hInput, buf);\r
6334     sel.cpMin = 999999;\r
6335     sel.cpMax = 999999;\r
6336     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6337     SetFocus(hInput);\r
6338   }\r
6339 }\r
6340 \r
6341 LRESULT CALLBACK \r
6342 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6343 {\r
6344   HWND hInput;\r
6345   CHARRANGE sel;\r
6346 \r
6347   switch (message) {\r
6348   case WM_KEYDOWN:\r
6349     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6350     switch (wParam) {\r
6351     case VK_PRIOR:\r
6352       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6353       return 0;\r
6354     case VK_NEXT:\r
6355       sel.cpMin = 999999;\r
6356       sel.cpMax = 999999;\r
6357       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6358       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6359       return 0;\r
6360     }\r
6361     break;\r
6362   case WM_CHAR:\r
6363    if(wParam != '\022') {\r
6364     if (wParam == '\t') {\r
6365       if (GetKeyState(VK_SHIFT) < 0) {\r
6366         /* shifted */\r
6367         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6368         if (buttonDesc[0].hwnd) {\r
6369           SetFocus(buttonDesc[0].hwnd);\r
6370         } else {\r
6371           SetFocus(hwndMain);\r
6372         }\r
6373       } else {\r
6374         /* unshifted */\r
6375         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6376       }\r
6377     } else {\r
6378       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6379       JAWS_DELETE( SetFocus(hInput); )\r
6380       SendMessage(hInput, message, wParam, lParam);\r
6381     }\r
6382     return 0;\r
6383    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6384   case WM_RBUTTONUP:\r
6385     if (GetKeyState(VK_SHIFT) & ~1) {\r
6386       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6387         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6388     } else {\r
6389       POINT pt;\r
6390       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6391       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6392       if (sel.cpMin == sel.cpMax) {\r
6393         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6394         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6395       }\r
6396       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6397         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6398       }\r
6399       pt.x = LOWORD(lParam);\r
6400       pt.y = HIWORD(lParam);\r
6401       MenuPopup(hwnd, pt, hmenu, -1);\r
6402     }\r
6403     return 0;\r
6404   case WM_PASTE:\r
6405     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6406     SetFocus(hInput);\r
6407     return SendMessage(hInput, message, wParam, lParam);\r
6408   case WM_MBUTTONDOWN:\r
6409     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6410   case WM_RBUTTONDOWN:\r
6411     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6412       /* Move selection here if it was empty */\r
6413       POINT pt;\r
6414       pt.x = LOWORD(lParam);\r
6415       pt.y = HIWORD(lParam);\r
6416       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6417       if (sel.cpMin == sel.cpMax) {\r
6418         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6419         sel.cpMax = sel.cpMin;\r
6420         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6421       }\r
6422       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6423     }\r
6424     return 0;\r
6425   case WM_COMMAND:\r
6426     switch (LOWORD(wParam)) {\r
6427     case IDM_QuickPaste:\r
6428       {\r
6429         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6430         if (sel.cpMin == sel.cpMax) {\r
6431           MessageBeep(MB_ICONEXCLAMATION);\r
6432           return 0;\r
6433         }\r
6434         SendMessage(hwnd, WM_COPY, 0, 0);\r
6435         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6436         SendMessage(hInput, WM_PASTE, 0, 0);\r
6437         SetFocus(hInput);\r
6438         return 0;\r
6439       }\r
6440     case IDM_Cut:\r
6441       SendMessage(hwnd, WM_CUT, 0, 0);\r
6442       return 0;\r
6443     case IDM_Paste:\r
6444       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6445       return 0;\r
6446     case IDM_Copy:\r
6447       SendMessage(hwnd, WM_COPY, 0, 0);\r
6448       return 0;\r
6449     default:\r
6450       {\r
6451         int i = LOWORD(wParam) - IDM_CommandX;\r
6452         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6453             icsTextMenuEntry[i].command != NULL) {\r
6454           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6455                    icsTextMenuEntry[i].getname,\r
6456                    icsTextMenuEntry[i].immediate);\r
6457           return 0;\r
6458         }\r
6459       }\r
6460       break;\r
6461     }\r
6462     break;\r
6463   }\r
6464   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6465 }\r
6466 \r
6467 WNDPROC consoleInputWindowProc;\r
6468 \r
6469 LRESULT CALLBACK\r
6470 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6471 {\r
6472   char buf[MSG_SIZ];\r
6473   char *p;\r
6474   static BOOL sendNextChar = FALSE;\r
6475   static BOOL quoteNextChar = FALSE;\r
6476   InputSource *is = consoleInputSource;\r
6477   CHARFORMAT cf;\r
6478   CHARRANGE sel;\r
6479 \r
6480   switch (message) {\r
6481   case WM_CHAR:\r
6482     if (!appData.localLineEditing || sendNextChar) {\r
6483       is->buf[0] = (CHAR) wParam;\r
6484       is->count = 1;\r
6485       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6486       sendNextChar = FALSE;\r
6487       return 0;\r
6488     }\r
6489     if (quoteNextChar) {\r
6490       buf[0] = (char) wParam;\r
6491       buf[1] = NULLCHAR;\r
6492       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6493       quoteNextChar = FALSE;\r
6494       return 0;\r
6495     }\r
6496     switch (wParam) {\r
6497     case '\r':   /* Enter key */\r
6498       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6499       if (consoleEcho) SaveInHistory(is->buf);\r
6500       is->buf[is->count++] = '\n';\r
6501       is->buf[is->count] = NULLCHAR;\r
6502       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6503       if (consoleEcho) {\r
6504         ConsoleOutput(is->buf, is->count, TRUE);\r
6505       } else if (appData.localLineEditing) {\r
6506         ConsoleOutput("\n", 1, TRUE);\r
6507       }\r
6508       /* fall thru */\r
6509     case '\033': /* Escape key */\r
6510       SetWindowText(hwnd, "");\r
6511       cf.cbSize = sizeof(CHARFORMAT);\r
6512       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6513       if (consoleEcho) {\r
6514         cf.crTextColor = textAttribs[ColorNormal].color;\r
6515       } else {\r
6516         cf.crTextColor = COLOR_ECHOOFF;\r
6517       }\r
6518       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6519       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6520       return 0;\r
6521     case '\t':   /* Tab key */\r
6522       if (GetKeyState(VK_SHIFT) < 0) {\r
6523         /* shifted */\r
6524         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6525       } else {\r
6526         /* unshifted */\r
6527         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6528         if (buttonDesc[0].hwnd) {\r
6529           SetFocus(buttonDesc[0].hwnd);\r
6530         } else {\r
6531           SetFocus(hwndMain);\r
6532         }\r
6533       }\r
6534       return 0;\r
6535     case '\023': /* Ctrl+S */\r
6536       sendNextChar = TRUE;\r
6537       return 0;\r
6538     case '\021': /* Ctrl+Q */\r
6539       quoteNextChar = TRUE;\r
6540       return 0;\r
6541     JAWS_REPLAY\r
6542     default:\r
6543       break;\r
6544     }\r
6545     break;\r
6546   case WM_KEYDOWN:\r
6547     switch (wParam) {\r
6548     case VK_UP:\r
6549       GetWindowText(hwnd, buf, MSG_SIZ);\r
6550       p = PrevInHistory(buf);\r
6551       if (p != NULL) {\r
6552         SetWindowText(hwnd, p);\r
6553         sel.cpMin = 999999;\r
6554         sel.cpMax = 999999;\r
6555         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6556         return 0;\r
6557       }\r
6558       break;\r
6559     case VK_DOWN:\r
6560       p = NextInHistory();\r
6561       if (p != NULL) {\r
6562         SetWindowText(hwnd, p);\r
6563         sel.cpMin = 999999;\r
6564         sel.cpMax = 999999;\r
6565         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6566         return 0;\r
6567       }\r
6568       break;\r
6569     case VK_HOME:\r
6570     case VK_END:\r
6571       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6572       /* fall thru */\r
6573     case VK_PRIOR:\r
6574     case VK_NEXT:\r
6575       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6576       return 0;\r
6577     }\r
6578     break;\r
6579   case WM_MBUTTONDOWN:\r
6580     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6581       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6582     break;\r
6583   case WM_RBUTTONUP:\r
6584     if (GetKeyState(VK_SHIFT) & ~1) {\r
6585       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6586         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6587     } else {\r
6588       POINT pt;\r
6589       HMENU hmenu;\r
6590       hmenu = LoadMenu(hInst, "InputMenu");\r
6591       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6592       if (sel.cpMin == sel.cpMax) {\r
6593         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6594         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6595       }\r
6596       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6597         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6598       }\r
6599       pt.x = LOWORD(lParam);\r
6600       pt.y = HIWORD(lParam);\r
6601       MenuPopup(hwnd, pt, hmenu, -1);\r
6602     }\r
6603     return 0;\r
6604   case WM_COMMAND:\r
6605     switch (LOWORD(wParam)) { \r
6606     case IDM_Undo:\r
6607       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6608       return 0;\r
6609     case IDM_SelectAll:\r
6610       sel.cpMin = 0;\r
6611       sel.cpMax = -1; /*999999?*/\r
6612       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6613       return 0;\r
6614     case IDM_Cut:\r
6615       SendMessage(hwnd, WM_CUT, 0, 0);\r
6616       return 0;\r
6617     case IDM_Paste:\r
6618       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6619       return 0;\r
6620     case IDM_Copy:\r
6621       SendMessage(hwnd, WM_COPY, 0, 0);\r
6622       return 0;\r
6623     }\r
6624     break;\r
6625   }\r
6626   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6627 }\r
6628 \r
6629 #define CO_MAX  100000\r
6630 #define CO_TRIM   1000\r
6631 \r
6632 LRESULT CALLBACK\r
6633 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6634 {\r
6635   static SnapData sd;\r
6636   HWND hText, hInput;\r
6637   RECT rect;\r
6638   static int sizeX, sizeY;\r
6639   int newSizeX, newSizeY;\r
6640   MINMAXINFO *mmi;\r
6641   WORD wMask;\r
6642 \r
6643   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6644   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6645 \r
6646   switch (message) {\r
6647   case WM_NOTIFY:\r
6648     if (((NMHDR*)lParam)->code == EN_LINK)\r
6649     {\r
6650       ENLINK *pLink = (ENLINK*)lParam;\r
6651       if (pLink->msg == WM_LBUTTONUP)\r
6652       {\r
6653         TEXTRANGE tr;\r
6654 \r
6655         tr.chrg = pLink->chrg;\r
6656         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
6657         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
6658         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
6659         free(tr.lpstrText);\r
6660       }\r
6661     }\r
6662     break;\r
6663   case WM_INITDIALOG: /* message: initialize dialog box */\r
6664     hwndConsole = hDlg;\r
6665     SetFocus(hInput);\r
6666     consoleTextWindowProc = (WNDPROC)\r
6667       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6668     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6669     consoleInputWindowProc = (WNDPROC)\r
6670       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6671     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6672     Colorize(ColorNormal, TRUE);\r
6673     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6674     ChangedConsoleFont();\r
6675     GetClientRect(hDlg, &rect);\r
6676     sizeX = rect.right;\r
6677     sizeY = rect.bottom;\r
6678     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
6679         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
6680       WINDOWPLACEMENT wp;\r
6681       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6682       wp.length = sizeof(WINDOWPLACEMENT);\r
6683       wp.flags = 0;\r
6684       wp.showCmd = SW_SHOW;\r
6685       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6686       wp.rcNormalPosition.left = wpConsole.x;\r
6687       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6688       wp.rcNormalPosition.top = wpConsole.y;\r
6689       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6690       SetWindowPlacement(hDlg, &wp);\r
6691     }\r
6692 \r
6693    // [HGM] Chessknight's change 2004-07-13\r
6694    else { /* Determine Defaults */\r
6695        WINDOWPLACEMENT wp;\r
6696        wpConsole.x = wpMain.width + 1;\r
6697        wpConsole.y = wpMain.y;\r
6698        wpConsole.width = screenWidth -  wpMain.width;\r
6699        wpConsole.height = wpMain.height;\r
6700        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6701        wp.length = sizeof(WINDOWPLACEMENT);\r
6702        wp.flags = 0;\r
6703        wp.showCmd = SW_SHOW;\r
6704        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6705        wp.rcNormalPosition.left = wpConsole.x;\r
6706        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6707        wp.rcNormalPosition.top = wpConsole.y;\r
6708        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6709        SetWindowPlacement(hDlg, &wp);\r
6710     }\r
6711 \r
6712    // Allow hText to highlight URLs and send notifications on them\r
6713    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
6714    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
6715    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
6716    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
6717 \r
6718     return FALSE;\r
6719 \r
6720   case WM_SETFOCUS:\r
6721     SetFocus(hInput);\r
6722     return 0;\r
6723 \r
6724   case WM_CLOSE:\r
6725     ExitEvent(0);\r
6726     /* not reached */\r
6727     break;\r
6728 \r
6729   case WM_SIZE:\r
6730     if (IsIconic(hDlg)) break;\r
6731     newSizeX = LOWORD(lParam);\r
6732     newSizeY = HIWORD(lParam);\r
6733     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6734       RECT rectText, rectInput;\r
6735       POINT pt;\r
6736       int newTextHeight, newTextWidth;\r
6737       GetWindowRect(hText, &rectText);\r
6738       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6739       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6740       if (newTextHeight < 0) {\r
6741         newSizeY += -newTextHeight;\r
6742         newTextHeight = 0;\r
6743       }\r
6744       SetWindowPos(hText, NULL, 0, 0,\r
6745         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6746       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6747       pt.x = rectInput.left;\r
6748       pt.y = rectInput.top + newSizeY - sizeY;\r
6749       ScreenToClient(hDlg, &pt);\r
6750       SetWindowPos(hInput, NULL, \r
6751         pt.x, pt.y, /* needs client coords */   \r
6752         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6753         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6754     }\r
6755     sizeX = newSizeX;\r
6756     sizeY = newSizeY;\r
6757     break;\r
6758 \r
6759   case WM_GETMINMAXINFO:\r
6760     /* Prevent resizing window too small */\r
6761     mmi = (MINMAXINFO *) lParam;\r
6762     mmi->ptMinTrackSize.x = 100;\r
6763     mmi->ptMinTrackSize.y = 100;\r
6764     break;\r
6765 \r
6766   /* [AS] Snapping */\r
6767   case WM_ENTERSIZEMOVE:\r
6768     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
6769 \r
6770   case WM_SIZING:\r
6771     return OnSizing( &sd, hDlg, wParam, lParam );\r
6772 \r
6773   case WM_MOVING:\r
6774     return OnMoving( &sd, hDlg, wParam, lParam );\r
6775 \r
6776   case WM_EXITSIZEMOVE:\r
6777         UpdateICSWidth(hText);\r
6778     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
6779   }\r
6780 \r
6781   return DefWindowProc(hDlg, message, wParam, lParam);\r
6782 }\r
6783 \r
6784 \r
6785 VOID\r
6786 ConsoleCreate()\r
6787 {\r
6788   HWND hCons;\r
6789   if (hwndConsole) return;\r
6790   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6791   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6792 }\r
6793 \r
6794 \r
6795 VOID\r
6796 ConsoleOutput(char* data, int length, int forceVisible)\r
6797 {\r
6798   HWND hText;\r
6799   int trim, exlen;\r
6800   char *p, *q;\r
6801   char buf[CO_MAX+1];\r
6802   POINT pEnd;\r
6803   RECT rect;\r
6804   static int delayLF = 0;\r
6805   CHARRANGE savesel, sel;\r
6806 \r
6807   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6808   p = data;\r
6809   q = buf;\r
6810   if (delayLF) {\r
6811     *q++ = '\r';\r
6812     *q++ = '\n';\r
6813     delayLF = 0;\r
6814   }\r
6815   while (length--) {\r
6816     if (*p == '\n') {\r
6817       if (*++p) {\r
6818         *q++ = '\r';\r
6819         *q++ = '\n';\r
6820       } else {\r
6821         delayLF = 1;\r
6822       }\r
6823     } else if (*p == '\007') {\r
6824        MyPlaySound(&sounds[(int)SoundBell]);\r
6825        p++;\r
6826     } else {\r
6827       *q++ = *p++;\r
6828     }\r
6829   }\r
6830   *q = NULLCHAR;\r
6831   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
6832   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6833   /* Save current selection */\r
6834   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
6835   exlen = GetWindowTextLength(hText);\r
6836   /* Find out whether current end of text is visible */\r
6837   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
6838   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
6839   /* Trim existing text if it's too long */\r
6840   if (exlen + (q - buf) > CO_MAX) {\r
6841     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
6842     sel.cpMin = 0;\r
6843     sel.cpMax = trim;\r
6844     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6845     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
6846     exlen -= trim;\r
6847     savesel.cpMin -= trim;\r
6848     savesel.cpMax -= trim;\r
6849     if (exlen < 0) exlen = 0;\r
6850     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
6851     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
6852   }\r
6853   /* Append the new text */\r
6854   sel.cpMin = exlen;\r
6855   sel.cpMax = exlen;\r
6856   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6857   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
6858   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
6859   if (forceVisible || exlen == 0 ||\r
6860       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
6861        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
6862     /* Scroll to make new end of text visible if old end of text\r
6863        was visible or new text is an echo of user typein */\r
6864     sel.cpMin = 9999999;\r
6865     sel.cpMax = 9999999;\r
6866     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6867     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6868     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
6869     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6870   }\r
6871   if (savesel.cpMax == exlen || forceVisible) {\r
6872     /* Move insert point to new end of text if it was at the old\r
6873        end of text or if the new text is an echo of user typein */\r
6874     sel.cpMin = 9999999;\r
6875     sel.cpMax = 9999999;\r
6876     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6877   } else {\r
6878     /* Restore previous selection */\r
6879     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
6880   }\r
6881   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6882 }\r
6883 \r
6884 /*---------*/\r
6885 \r
6886 \r
6887 void\r
6888 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
6889 {\r
6890   char buf[100];\r
6891   char *str;\r
6892   COLORREF oldFg, oldBg;\r
6893   HFONT oldFont;\r
6894   RECT rect;\r
6895 \r
6896   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
6897 \r
6898   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6899   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6900   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6901 \r
6902   rect.left = x;\r
6903   rect.right = x + squareSize;\r
6904   rect.top  = y;\r
6905   rect.bottom = y + squareSize;\r
6906   str = buf;\r
6907 \r
6908   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
6909                     + (rightAlign ? (squareSize*2)/3 : 0),\r
6910              y, ETO_CLIPPED|ETO_OPAQUE,\r
6911              &rect, str, strlen(str), NULL);\r
6912 \r
6913   (void) SetTextColor(hdc, oldFg);\r
6914   (void) SetBkColor(hdc, oldBg);\r
6915   (void) SelectObject(hdc, oldFont);\r
6916 }\r
6917 \r
6918 void\r
6919 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
6920               RECT *rect, char *color, char *flagFell)\r
6921 {\r
6922   char buf[100];\r
6923   char *str;\r
6924   COLORREF oldFg, oldBg;\r
6925   HFONT oldFont;\r
6926 \r
6927   if (appData.clockMode) {\r
6928     if (tinyLayout)\r
6929       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
6930     else\r
6931       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
6932     str = buf;\r
6933   } else {\r
6934     str = color;\r
6935   }\r
6936 \r
6937   if (highlight) {\r
6938     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6939     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6940   } else {\r
6941     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
6942     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
6943   }\r
6944   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6945 \r
6946   JAWS_SILENCE\r
6947 \r
6948   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6949              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
6950              rect, str, strlen(str), NULL);\r
6951   if(logoHeight > 0 && appData.clockMode) {\r
6952       RECT r;\r
6953       sprintf(buf, "%s %s", buf+7, flagFell);\r
6954       r.top = rect->top + logoHeight/2;\r
6955       r.left = rect->left;\r
6956       r.right = rect->right;\r
6957       r.bottom = rect->bottom;\r
6958       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6959                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
6960                  &r, str, strlen(str), NULL);\r
6961   }\r
6962   (void) SetTextColor(hdc, oldFg);\r
6963   (void) SetBkColor(hdc, oldBg);\r
6964   (void) SelectObject(hdc, oldFont);\r
6965 }\r
6966 \r
6967 \r
6968 int\r
6969 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
6970            OVERLAPPED *ovl)\r
6971 {\r
6972   int ok, err;\r
6973 \r
6974   /* [AS]  */\r
6975   if( count <= 0 ) {\r
6976     if (appData.debugMode) {\r
6977       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
6978     }\r
6979 \r
6980     return ERROR_INVALID_USER_BUFFER;\r
6981   }\r
6982 \r
6983   ResetEvent(ovl->hEvent);\r
6984   ovl->Offset = ovl->OffsetHigh = 0;\r
6985   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
6986   if (ok) {\r
6987     err = NO_ERROR;\r
6988   } else {\r
6989     err = GetLastError();\r
6990     if (err == ERROR_IO_PENDING) {\r
6991       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
6992       if (ok)\r
6993         err = NO_ERROR;\r
6994       else\r
6995         err = GetLastError();\r
6996     }\r
6997   }\r
6998   return err;\r
6999 }\r
7000 \r
7001 int\r
7002 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7003             OVERLAPPED *ovl)\r
7004 {\r
7005   int ok, err;\r
7006 \r
7007   ResetEvent(ovl->hEvent);\r
7008   ovl->Offset = ovl->OffsetHigh = 0;\r
7009   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7010   if (ok) {\r
7011     err = NO_ERROR;\r
7012   } else {\r
7013     err = GetLastError();\r
7014     if (err == ERROR_IO_PENDING) {\r
7015       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7016       if (ok)\r
7017         err = NO_ERROR;\r
7018       else\r
7019         err = GetLastError();\r
7020     }\r
7021   }\r
7022   return err;\r
7023 }\r
7024 \r
7025 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7026 void CheckForInputBufferFull( InputSource * is )\r
7027 {\r
7028     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7029         /* Look for end of line */\r
7030         char * p = is->buf;\r
7031         \r
7032         while( p < is->next && *p != '\n' ) {\r
7033             p++;\r
7034         }\r
7035 \r
7036         if( p >= is->next ) {\r
7037             if (appData.debugMode) {\r
7038                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7039             }\r
7040 \r
7041             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7042             is->count = (DWORD) -1;\r
7043             is->next = is->buf;\r
7044         }\r
7045     }\r
7046 }\r
7047 \r
7048 DWORD\r
7049 InputThread(LPVOID arg)\r
7050 {\r
7051   InputSource *is;\r
7052   OVERLAPPED ovl;\r
7053 \r
7054   is = (InputSource *) arg;\r
7055   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7056   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7057   while (is->hThread != NULL) {\r
7058     is->error = DoReadFile(is->hFile, is->next,\r
7059                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7060                            &is->count, &ovl);\r
7061     if (is->error == NO_ERROR) {\r
7062       is->next += is->count;\r
7063     } else {\r
7064       if (is->error == ERROR_BROKEN_PIPE) {\r
7065         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7066         is->count = 0;\r
7067       } else {\r
7068         is->count = (DWORD) -1;\r
7069         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7070         break; \r
7071       }\r
7072     }\r
7073 \r
7074     CheckForInputBufferFull( is );\r
7075 \r
7076     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7077 \r
7078     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7079 \r
7080     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7081   }\r
7082 \r
7083   CloseHandle(ovl.hEvent);\r
7084   CloseHandle(is->hFile);\r
7085 \r
7086   if (appData.debugMode) {\r
7087     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7088   }\r
7089 \r
7090   return 0;\r
7091 }\r
7092 \r
7093 \r
7094 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7095 DWORD\r
7096 NonOvlInputThread(LPVOID arg)\r
7097 {\r
7098   InputSource *is;\r
7099   char *p, *q;\r
7100   int i;\r
7101   char prev;\r
7102 \r
7103   is = (InputSource *) arg;\r
7104   while (is->hThread != NULL) {\r
7105     is->error = ReadFile(is->hFile, is->next,\r
7106                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7107                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7108     if (is->error == NO_ERROR) {\r
7109       /* Change CRLF to LF */\r
7110       if (is->next > is->buf) {\r
7111         p = is->next - 1;\r
7112         i = is->count + 1;\r
7113       } else {\r
7114         p = is->next;\r
7115         i = is->count;\r
7116       }\r
7117       q = p;\r
7118       prev = NULLCHAR;\r
7119       while (i > 0) {\r
7120         if (prev == '\r' && *p == '\n') {\r
7121           *(q-1) = '\n';\r
7122           is->count--;\r
7123         } else { \r
7124           *q++ = *p;\r
7125         }\r
7126         prev = *p++;\r
7127         i--;\r
7128       }\r
7129       *q = NULLCHAR;\r
7130       is->next = q;\r
7131     } else {\r
7132       if (is->error == ERROR_BROKEN_PIPE) {\r
7133         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7134         is->count = 0; \r
7135       } else {\r
7136         is->count = (DWORD) -1;\r
7137       }\r
7138     }\r
7139 \r
7140     CheckForInputBufferFull( is );\r
7141 \r
7142     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7143 \r
7144     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7145 \r
7146     if (is->count < 0) break;  /* Quit on error */\r
7147   }\r
7148   CloseHandle(is->hFile);\r
7149   return 0;\r
7150 }\r
7151 \r
7152 DWORD\r
7153 SocketInputThread(LPVOID arg)\r
7154 {\r
7155   InputSource *is;\r
7156 \r
7157   is = (InputSource *) arg;\r
7158   while (is->hThread != NULL) {\r
7159     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7160     if ((int)is->count == SOCKET_ERROR) {\r
7161       is->count = (DWORD) -1;\r
7162       is->error = WSAGetLastError();\r
7163     } else {\r
7164       is->error = NO_ERROR;\r
7165       is->next += is->count;\r
7166       if (is->count == 0 && is->second == is) {\r
7167         /* End of file on stderr; quit with no message */\r
7168         break;\r
7169       }\r
7170     }\r
7171     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7172 \r
7173     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7174 \r
7175     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7176   }\r
7177   return 0;\r
7178 }\r
7179 \r
7180 VOID\r
7181 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7182 {\r
7183   InputSource *is;\r
7184 \r
7185   is = (InputSource *) lParam;\r
7186   if (is->lineByLine) {\r
7187     /* Feed in lines one by one */\r
7188     char *p = is->buf;\r
7189     char *q = p;\r
7190     while (q < is->next) {\r
7191       if (*q++ == '\n') {\r
7192         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7193         p = q;\r
7194       }\r
7195     }\r
7196     \r
7197     /* Move any partial line to the start of the buffer */\r
7198     q = is->buf;\r
7199     while (p < is->next) {\r
7200       *q++ = *p++;\r
7201     }\r
7202     is->next = q;\r
7203 \r
7204     if (is->error != NO_ERROR || is->count == 0) {\r
7205       /* Notify backend of the error.  Note: If there was a partial\r
7206          line at the end, it is not flushed through. */\r
7207       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7208     }\r
7209   } else {\r
7210     /* Feed in the whole chunk of input at once */\r
7211     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7212     is->next = is->buf;\r
7213   }\r
7214 }\r
7215 \r
7216 /*---------------------------------------------------------------------------*\\r
7217  *\r
7218  *  Menu enables. Used when setting various modes.\r
7219  *\r
7220 \*---------------------------------------------------------------------------*/\r
7221 \r
7222 typedef struct {\r
7223   int item;\r
7224   int flags;\r
7225 } Enables;\r
7226 \r
7227 VOID\r
7228 GreyRevert(Boolean grey)\r
7229 { // [HGM] vari: for retracting variations in local mode\r
7230   HMENU hmenu = GetMenu(hwndMain);\r
7231   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7232 }\r
7233 \r
7234 VOID\r
7235 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7236 {\r
7237   while (enab->item > 0) {\r
7238     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7239     enab++;\r
7240   }\r
7241 }\r
7242 \r
7243 Enables gnuEnables[] = {\r
7244   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7245   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7246   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7247   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7248   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7249   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7250   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7251   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7252   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7253   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7254   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7255   { -1, -1 }\r
7256 };\r
7257 \r
7258 Enables icsEnables[] = {\r
7259   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7260   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7261   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7262   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7263   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7264   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7265   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7266   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7267   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7268   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7269   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7270   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7271   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7272   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7273   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7274   { -1, -1 }\r
7275 };\r
7276 \r
7277 #ifdef ZIPPY\r
7278 Enables zippyEnables[] = {\r
7279   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7280   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7281   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7282   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7283   { -1, -1 }\r
7284 };\r
7285 #endif\r
7286 \r
7287 Enables ncpEnables[] = {\r
7288   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7289   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7290   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7291   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7292   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7293   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7294   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7295   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7296   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7297   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7298   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7299   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7300   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7301   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7302   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7303   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7304   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7305   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7306   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7307   { -1, -1 }\r
7308 };\r
7309 \r
7310 Enables trainingOnEnables[] = {\r
7311   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7312   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7313   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7314   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7315   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7316   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7317   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7318   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7319   { -1, -1 }\r
7320 };\r
7321 \r
7322 Enables trainingOffEnables[] = {\r
7323   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7324   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7325   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7326   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7327   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7328   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7329   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7330   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7331   { -1, -1 }\r
7332 };\r
7333 \r
7334 /* These modify either ncpEnables or gnuEnables */\r
7335 Enables cmailEnables[] = {\r
7336   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7337   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7338   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7339   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7340   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7341   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7342   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7343   { -1, -1 }\r
7344 };\r
7345 \r
7346 Enables machineThinkingEnables[] = {\r
7347   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7348   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7349   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7350   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7351   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7352   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7353   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7354   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7355   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7356   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7357   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7358   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7359   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7360   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7361   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7362   { -1, -1 }\r
7363 };\r
7364 \r
7365 Enables userThinkingEnables[] = {\r
7366   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7367   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7368   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7369   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7370   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7371   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7372   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7373   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7374   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7375   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7376   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7377   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7378   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7379   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7380   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7381   { -1, -1 }\r
7382 };\r
7383 \r
7384 /*---------------------------------------------------------------------------*\\r
7385  *\r
7386  *  Front-end interface functions exported by XBoard.\r
7387  *  Functions appear in same order as prototypes in frontend.h.\r
7388  * \r
7389 \*---------------------------------------------------------------------------*/\r
7390 VOID\r
7391 ModeHighlight()\r
7392 {\r
7393   static UINT prevChecked = 0;\r
7394   static int prevPausing = 0;\r
7395   UINT nowChecked;\r
7396 \r
7397   if (pausing != prevPausing) {\r
7398     prevPausing = pausing;\r
7399     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7400                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7401     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7402   }\r
7403 \r
7404   switch (gameMode) {\r
7405   case BeginningOfGame:\r
7406     if (appData.icsActive)\r
7407       nowChecked = IDM_IcsClient;\r
7408     else if (appData.noChessProgram)\r
7409       nowChecked = IDM_EditGame;\r
7410     else\r
7411       nowChecked = IDM_MachineBlack;\r
7412     break;\r
7413   case MachinePlaysBlack:\r
7414     nowChecked = IDM_MachineBlack;\r
7415     break;\r
7416   case MachinePlaysWhite:\r
7417     nowChecked = IDM_MachineWhite;\r
7418     break;\r
7419   case TwoMachinesPlay:\r
7420     nowChecked = IDM_TwoMachines;\r
7421     break;\r
7422   case AnalyzeMode:\r
7423     nowChecked = IDM_AnalysisMode;\r
7424     break;\r
7425   case AnalyzeFile:\r
7426     nowChecked = IDM_AnalyzeFile;\r
7427     break;\r
7428   case EditGame:\r
7429     nowChecked = IDM_EditGame;\r
7430     break;\r
7431   case PlayFromGameFile:\r
7432     nowChecked = IDM_LoadGame;\r
7433     break;\r
7434   case EditPosition:\r
7435     nowChecked = IDM_EditPosition;\r
7436     break;\r
7437   case Training:\r
7438     nowChecked = IDM_Training;\r
7439     break;\r
7440   case IcsPlayingWhite:\r
7441   case IcsPlayingBlack:\r
7442   case IcsObserving:\r
7443   case IcsIdle:\r
7444     nowChecked = IDM_IcsClient;\r
7445     break;\r
7446   default:\r
7447   case EndOfGame:\r
7448     nowChecked = 0;\r
7449     break;\r
7450   }\r
7451   if (prevChecked != 0)\r
7452     (void) CheckMenuItem(GetMenu(hwndMain),\r
7453                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7454   if (nowChecked != 0)\r
7455     (void) CheckMenuItem(GetMenu(hwndMain),\r
7456                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7457 \r
7458   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7459     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7460                           MF_BYCOMMAND|MF_ENABLED);\r
7461   } else {\r
7462     (void) EnableMenuItem(GetMenu(hwndMain), \r
7463                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7464   }\r
7465 \r
7466   prevChecked = nowChecked;\r
7467 \r
7468   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7469   if (appData.icsActive) {\r
7470        if (appData.icsEngineAnalyze) {\r
7471                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7472                        MF_BYCOMMAND|MF_CHECKED);\r
7473        } else {\r
7474                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7475                        MF_BYCOMMAND|MF_UNCHECKED);\r
7476        }\r
7477   }\r
7478 }\r
7479 \r
7480 VOID\r
7481 SetICSMode()\r
7482 {\r
7483   HMENU hmenu = GetMenu(hwndMain);\r
7484   SetMenuEnables(hmenu, icsEnables);\r
7485   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7486     MF_BYPOSITION|MF_ENABLED);\r
7487 #ifdef ZIPPY\r
7488   if (appData.zippyPlay) {\r
7489     SetMenuEnables(hmenu, zippyEnables);\r
7490     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7491          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7492           MF_BYCOMMAND|MF_ENABLED);\r
7493   }\r
7494 #endif\r
7495 }\r
7496 \r
7497 VOID\r
7498 SetGNUMode()\r
7499 {\r
7500   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7501 }\r
7502 \r
7503 VOID\r
7504 SetNCPMode()\r
7505 {\r
7506   HMENU hmenu = GetMenu(hwndMain);\r
7507   SetMenuEnables(hmenu, ncpEnables);\r
7508   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7509     MF_BYPOSITION|MF_GRAYED);\r
7510     DrawMenuBar(hwndMain);\r
7511 }\r
7512 \r
7513 VOID\r
7514 SetCmailMode()\r
7515 {\r
7516   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7517 }\r
7518 \r
7519 VOID \r
7520 SetTrainingModeOn()\r
7521 {\r
7522   int i;\r
7523   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7524   for (i = 0; i < N_BUTTONS; i++) {\r
7525     if (buttonDesc[i].hwnd != NULL)\r
7526       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7527   }\r
7528   CommentPopDown();\r
7529 }\r
7530 \r
7531 VOID SetTrainingModeOff()\r
7532 {\r
7533   int i;\r
7534   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7535   for (i = 0; i < N_BUTTONS; i++) {\r
7536     if (buttonDesc[i].hwnd != NULL)\r
7537       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7538   }\r
7539 }\r
7540 \r
7541 \r
7542 VOID\r
7543 SetUserThinkingEnables()\r
7544 {\r
7545   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7546 }\r
7547 \r
7548 VOID\r
7549 SetMachineThinkingEnables()\r
7550 {\r
7551   HMENU hMenu = GetMenu(hwndMain);\r
7552   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7553 \r
7554   SetMenuEnables(hMenu, machineThinkingEnables);\r
7555 \r
7556   if (gameMode == MachinePlaysBlack) {\r
7557     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7558   } else if (gameMode == MachinePlaysWhite) {\r
7559     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7560   } else if (gameMode == TwoMachinesPlay) {\r
7561     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7562   }\r
7563 }\r
7564 \r
7565 \r
7566 VOID\r
7567 DisplayTitle(char *str)\r
7568 {\r
7569   char title[MSG_SIZ], *host;\r
7570   if (str[0] != NULLCHAR) {\r
7571     strcpy(title, str);\r
7572   } else if (appData.icsActive) {\r
7573     if (appData.icsCommPort[0] != NULLCHAR)\r
7574       host = "ICS";\r
7575     else \r
7576       host = appData.icsHost;\r
7577     sprintf(title, "%s: %s", szTitle, host);\r
7578   } else if (appData.noChessProgram) {\r
7579     strcpy(title, szTitle);\r
7580   } else {\r
7581     strcpy(title, szTitle);\r
7582     strcat(title, ": ");\r
7583     strcat(title, first.tidy);\r
7584   }\r
7585   SetWindowText(hwndMain, title);\r
7586 }\r
7587 \r
7588 \r
7589 VOID\r
7590 DisplayMessage(char *str1, char *str2)\r
7591 {\r
7592   HDC hdc;\r
7593   HFONT oldFont;\r
7594   int remain = MESSAGE_TEXT_MAX - 1;\r
7595   int len;\r
7596 \r
7597   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7598   messageText[0] = NULLCHAR;\r
7599   if (*str1) {\r
7600     len = strlen(str1);\r
7601     if (len > remain) len = remain;\r
7602     strncpy(messageText, str1, len);\r
7603     messageText[len] = NULLCHAR;\r
7604     remain -= len;\r
7605   }\r
7606   if (*str2 && remain >= 2) {\r
7607     if (*str1) {\r
7608       strcat(messageText, "  ");\r
7609       remain -= 2;\r
7610     }\r
7611     len = strlen(str2);\r
7612     if (len > remain) len = remain;\r
7613     strncat(messageText, str2, len);\r
7614   }\r
7615   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7616 \r
7617   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
7618 \r
7619   SAYMACHINEMOVE();\r
7620 \r
7621   hdc = GetDC(hwndMain);\r
7622   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7623   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7624              &messageRect, messageText, strlen(messageText), NULL);\r
7625   (void) SelectObject(hdc, oldFont);\r
7626   (void) ReleaseDC(hwndMain, hdc);\r
7627 }\r
7628 \r
7629 VOID\r
7630 DisplayError(char *str, int error)\r
7631 {\r
7632   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7633   int len;\r
7634 \r
7635   if (error == 0) {\r
7636     strcpy(buf, str);\r
7637   } else {\r
7638     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7639                         NULL, error, LANG_NEUTRAL,\r
7640                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7641     if (len > 0) {\r
7642       sprintf(buf, "%s:\n%s", str, buf2);\r
7643     } else {\r
7644       ErrorMap *em = errmap;\r
7645       while (em->err != 0 && em->err != error) em++;\r
7646       if (em->err != 0) {\r
7647         sprintf(buf, "%s:\n%s", str, em->msg);\r
7648       } else {\r
7649         sprintf(buf, "%s:\nError code %d", str, error);\r
7650       }\r
7651     }\r
7652   }\r
7653   \r
7654   ErrorPopUp("Error", buf);\r
7655 }\r
7656 \r
7657 \r
7658 VOID\r
7659 DisplayMoveError(char *str)\r
7660 {\r
7661   fromX = fromY = -1;\r
7662   ClearHighlights();\r
7663   DrawPosition(FALSE, NULL);\r
7664   if (appData.popupMoveErrors) {\r
7665     ErrorPopUp("Error", str);\r
7666   } else {\r
7667     DisplayMessage(str, "");\r
7668     moveErrorMessageUp = TRUE;\r
7669   }\r
7670 }\r
7671 \r
7672 VOID\r
7673 DisplayFatalError(char *str, int error, int exitStatus)\r
7674 {\r
7675   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7676   int len;\r
7677   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7678 \r
7679   if (error != 0) {\r
7680     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7681                         NULL, error, LANG_NEUTRAL,\r
7682                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7683     if (len > 0) {\r
7684       sprintf(buf, "%s:\n%s", str, buf2);\r
7685     } else {\r
7686       ErrorMap *em = errmap;\r
7687       while (em->err != 0 && em->err != error) em++;\r
7688       if (em->err != 0) {\r
7689         sprintf(buf, "%s:\n%s", str, em->msg);\r
7690       } else {\r
7691         sprintf(buf, "%s:\nError code %d", str, error);\r
7692       }\r
7693     }\r
7694     str = buf;\r
7695   }\r
7696   if (appData.debugMode) {\r
7697     fprintf(debugFP, "%s: %s\n", label, str);\r
7698   }\r
7699   if (appData.popupExitMessage) {\r
7700     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7701                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7702   }\r
7703   ExitEvent(exitStatus);\r
7704 }\r
7705 \r
7706 \r
7707 VOID\r
7708 DisplayInformation(char *str)\r
7709 {\r
7710   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7711 }\r
7712 \r
7713 \r
7714 VOID\r
7715 DisplayNote(char *str)\r
7716 {\r
7717   ErrorPopUp("Note", str);\r
7718 }\r
7719 \r
7720 \r
7721 typedef struct {\r
7722   char *title, *question, *replyPrefix;\r
7723   ProcRef pr;\r
7724 } QuestionParams;\r
7725 \r
7726 LRESULT CALLBACK\r
7727 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7728 {\r
7729   static QuestionParams *qp;\r
7730   char reply[MSG_SIZ];\r
7731   int len, err;\r
7732 \r
7733   switch (message) {\r
7734   case WM_INITDIALOG:\r
7735     qp = (QuestionParams *) lParam;\r
7736     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7737     SetWindowText(hDlg, qp->title);\r
7738     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7739     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7740     return FALSE;\r
7741 \r
7742   case WM_COMMAND:\r
7743     switch (LOWORD(wParam)) {\r
7744     case IDOK:\r
7745       strcpy(reply, qp->replyPrefix);\r
7746       if (*reply) strcat(reply, " ");\r
7747       len = strlen(reply);\r
7748       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7749       strcat(reply, "\n");\r
7750       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7751       EndDialog(hDlg, TRUE);\r
7752       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7753       return TRUE;\r
7754     case IDCANCEL:\r
7755       EndDialog(hDlg, FALSE);\r
7756       return TRUE;\r
7757     default:\r
7758       break;\r
7759     }\r
7760     break;\r
7761   }\r
7762   return FALSE;\r
7763 }\r
7764 \r
7765 VOID\r
7766 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7767 {\r
7768     QuestionParams qp;\r
7769     FARPROC lpProc;\r
7770     \r
7771     qp.title = title;\r
7772     qp.question = question;\r
7773     qp.replyPrefix = replyPrefix;\r
7774     qp.pr = pr;\r
7775     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7776     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7777       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7778     FreeProcInstance(lpProc);\r
7779 }\r
7780 \r
7781 /* [AS] Pick FRC position */\r
7782 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7783 {\r
7784     static int * lpIndexFRC;\r
7785     BOOL index_is_ok;\r
7786     char buf[16];\r
7787 \r
7788     switch( message )\r
7789     {\r
7790     case WM_INITDIALOG:\r
7791         lpIndexFRC = (int *) lParam;\r
7792 \r
7793         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7794 \r
7795         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
7796         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
7797         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
7798         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
7799 \r
7800         break;\r
7801 \r
7802     case WM_COMMAND:\r
7803         switch( LOWORD(wParam) ) {\r
7804         case IDOK:\r
7805             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7806             EndDialog( hDlg, 0 );\r
7807             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
7808             return TRUE;\r
7809         case IDCANCEL:\r
7810             EndDialog( hDlg, 1 );   \r
7811             return TRUE;\r
7812         case IDC_NFG_Edit:\r
7813             if( HIWORD(wParam) == EN_CHANGE ) {\r
7814                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7815 \r
7816                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
7817             }\r
7818             return TRUE;\r
7819         case IDC_NFG_Random:\r
7820             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
7821             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
7822             return TRUE;\r
7823         }\r
7824 \r
7825         break;\r
7826     }\r
7827 \r
7828     return FALSE;\r
7829 }\r
7830 \r
7831 int NewGameFRC()\r
7832 {\r
7833     int result;\r
7834     int index = appData.defaultFrcPosition;\r
7835     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
7836 \r
7837     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
7838 \r
7839     if( result == 0 ) {\r
7840         appData.defaultFrcPosition = index;\r
7841     }\r
7842 \r
7843     return result;\r
7844 }\r
7845 \r
7846 /* [AS] Game list options. Refactored by HGM */\r
7847 \r
7848 HWND gameListOptionsDialog;\r
7849 \r
7850 // low-level front-end: clear text edit / list widget\r
7851 void\r
7852 GLT_ClearList()\r
7853 {\r
7854     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
7855 }\r
7856 \r
7857 // low-level front-end: clear text edit / list widget\r
7858 void\r
7859 GLT_DeSelectList()\r
7860 {\r
7861     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
7862 }\r
7863 \r
7864 // low-level front-end: append line to text edit / list widget\r
7865 void\r
7866 GLT_AddToList( char *name )\r
7867 {\r
7868     if( name != 0 ) {\r
7869             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
7870     }\r
7871 }\r
7872 \r
7873 // low-level front-end: get line from text edit / list widget\r
7874 Boolean\r
7875 GLT_GetFromList( int index, char *name )\r
7876 {\r
7877     if( name != 0 ) {\r
7878             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
7879                 return TRUE;\r
7880     }\r
7881     return FALSE;\r
7882 }\r
7883 \r
7884 void GLT_MoveSelection( HWND hDlg, int delta )\r
7885 {\r
7886     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
7887     int idx2 = idx1 + delta;\r
7888     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
7889 \r
7890     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
7891         char buf[128];\r
7892 \r
7893         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
7894         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
7895         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
7896         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
7897     }\r
7898 }\r
7899 \r
7900 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7901 {\r
7902     switch( message )\r
7903     {\r
7904     case WM_INITDIALOG:\r
7905         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
7906         \r
7907         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7908 \r
7909         /* Initialize list */\r
7910         GLT_TagsToList( lpUserGLT );\r
7911 \r
7912         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
7913 \r
7914         break;\r
7915 \r
7916     case WM_COMMAND:\r
7917         switch( LOWORD(wParam) ) {\r
7918         case IDOK:\r
7919             GLT_ParseList();\r
7920             EndDialog( hDlg, 0 );\r
7921             return TRUE;\r
7922         case IDCANCEL:\r
7923             EndDialog( hDlg, 1 );\r
7924             return TRUE;\r
7925 \r
7926         case IDC_GLT_Default:\r
7927             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
7928             return TRUE;\r
7929 \r
7930         case IDC_GLT_Restore:\r
7931             GLT_TagsToList( appData.gameListTags );\r
7932             return TRUE;\r
7933 \r
7934         case IDC_GLT_Up:\r
7935             GLT_MoveSelection( hDlg, -1 );\r
7936             return TRUE;\r
7937 \r
7938         case IDC_GLT_Down:\r
7939             GLT_MoveSelection( hDlg, +1 );\r
7940             return TRUE;\r
7941         }\r
7942 \r
7943         break;\r
7944     }\r
7945 \r
7946     return FALSE;\r
7947 }\r
7948 \r
7949 int GameListOptions()\r
7950 {\r
7951     int result;\r
7952     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
7953 \r
7954     strcpy( lpUserGLT, appData.gameListTags );\r
7955 \r
7956     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
7957 \r
7958     if( result == 0 ) {\r
7959         /* [AS] Memory leak here! */\r
7960         appData.gameListTags = strdup( lpUserGLT ); \r
7961     }\r
7962 \r
7963     return result;\r
7964 }\r
7965 \r
7966 VOID\r
7967 DisplayIcsInteractionTitle(char *str)\r
7968 {\r
7969   char consoleTitle[MSG_SIZ];\r
7970 \r
7971   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
7972   SetWindowText(hwndConsole, consoleTitle);\r
7973 }\r
7974 \r
7975 void\r
7976 DrawPosition(int fullRedraw, Board board)\r
7977 {\r
7978   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
7979 }\r
7980 \r
7981 void NotifyFrontendLogin()\r
7982 {\r
7983         if (hwndConsole)\r
7984                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7985 }\r
7986 \r
7987 VOID\r
7988 ResetFrontEnd()\r
7989 {\r
7990   fromX = fromY = -1;\r
7991   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
7992     dragInfo.pos.x = dragInfo.pos.y = -1;\r
7993     dragInfo.pos.x = dragInfo.pos.y = -1;\r
7994     dragInfo.lastpos = dragInfo.pos;\r
7995     dragInfo.start.x = dragInfo.start.y = -1;\r
7996     dragInfo.from = dragInfo.start;\r
7997     ReleaseCapture();\r
7998     DrawPosition(TRUE, NULL);\r
7999   }\r
8000 }\r
8001 \r
8002 \r
8003 VOID\r
8004 CommentPopUp(char *title, char *str)\r
8005 {\r
8006   HWND hwnd = GetActiveWindow();\r
8007   EitherCommentPopUp(0, title, str, FALSE);\r
8008   SAY(str);\r
8009   SetActiveWindow(hwnd);\r
8010 }\r
8011 \r
8012 VOID\r
8013 CommentPopDown(void)\r
8014 {\r
8015   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8016   if (commentDialog) {\r
8017     ShowWindow(commentDialog, SW_HIDE);\r
8018   }\r
8019   commentUp = FALSE;\r
8020 }\r
8021 \r
8022 VOID\r
8023 EditCommentPopUp(int index, char *title, char *str)\r
8024 {\r
8025   EitherCommentPopUp(index, title, str, TRUE);\r
8026 }\r
8027 \r
8028 \r
8029 VOID\r
8030 RingBell()\r
8031 {\r
8032   MyPlaySound(&sounds[(int)SoundMove]);\r
8033 }\r
8034 \r
8035 VOID PlayIcsWinSound()\r
8036 {\r
8037   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8038 }\r
8039 \r
8040 VOID PlayIcsLossSound()\r
8041 {\r
8042   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8043 }\r
8044 \r
8045 VOID PlayIcsDrawSound()\r
8046 {\r
8047   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8048 }\r
8049 \r
8050 VOID PlayIcsUnfinishedSound()\r
8051 {\r
8052   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8053 }\r
8054 \r
8055 VOID\r
8056 PlayAlarmSound()\r
8057 {\r
8058   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8059 }\r
8060 \r
8061 \r
8062 VOID\r
8063 EchoOn()\r
8064 {\r
8065   HWND hInput;\r
8066   consoleEcho = TRUE;\r
8067   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8068   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8069   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8070 }\r
8071 \r
8072 \r
8073 VOID\r
8074 EchoOff()\r
8075 {\r
8076   CHARFORMAT cf;\r
8077   HWND hInput;\r
8078   consoleEcho = FALSE;\r
8079   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8080   /* This works OK: set text and background both to the same color */\r
8081   cf = consoleCF;\r
8082   cf.crTextColor = COLOR_ECHOOFF;\r
8083   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8084   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8085 }\r
8086 \r
8087 /* No Raw()...? */\r
8088 \r
8089 void Colorize(ColorClass cc, int continuation)\r
8090 {\r
8091   currentColorClass = cc;\r
8092   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8093   consoleCF.crTextColor = textAttribs[cc].color;\r
8094   consoleCF.dwEffects = textAttribs[cc].effects;\r
8095   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8096 }\r
8097 \r
8098 char *\r
8099 UserName()\r
8100 {\r
8101   static char buf[MSG_SIZ];\r
8102   DWORD bufsiz = MSG_SIZ;\r
8103 \r
8104   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8105         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8106   }\r
8107   if (!GetUserName(buf, &bufsiz)) {\r
8108     /*DisplayError("Error getting user name", GetLastError());*/\r
8109     strcpy(buf, "User");\r
8110   }\r
8111   return buf;\r
8112 }\r
8113 \r
8114 char *\r
8115 HostName()\r
8116 {\r
8117   static char buf[MSG_SIZ];\r
8118   DWORD bufsiz = MSG_SIZ;\r
8119 \r
8120   if (!GetComputerName(buf, &bufsiz)) {\r
8121     /*DisplayError("Error getting host name", GetLastError());*/\r
8122     strcpy(buf, "Unknown");\r
8123   }\r
8124   return buf;\r
8125 }\r
8126 \r
8127 \r
8128 int\r
8129 ClockTimerRunning()\r
8130 {\r
8131   return clockTimerEvent != 0;\r
8132 }\r
8133 \r
8134 int\r
8135 StopClockTimer()\r
8136 {\r
8137   if (clockTimerEvent == 0) return FALSE;\r
8138   KillTimer(hwndMain, clockTimerEvent);\r
8139   clockTimerEvent = 0;\r
8140   return TRUE;\r
8141 }\r
8142 \r
8143 void\r
8144 StartClockTimer(long millisec)\r
8145 {\r
8146   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8147                              (UINT) millisec, NULL);\r
8148 }\r
8149 \r
8150 void\r
8151 DisplayWhiteClock(long timeRemaining, int highlight)\r
8152 {\r
8153   HDC hdc;\r
8154   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8155 \r
8156   if(appData.noGUI) return;\r
8157   hdc = GetDC(hwndMain);\r
8158   if (!IsIconic(hwndMain)) {\r
8159     DisplayAClock(hdc, timeRemaining, highlight, \r
8160                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
8161   }\r
8162   if (highlight && iconCurrent == iconBlack) {\r
8163     iconCurrent = iconWhite;\r
8164     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8165     if (IsIconic(hwndMain)) {\r
8166       DrawIcon(hdc, 2, 2, iconCurrent);\r
8167     }\r
8168   }\r
8169   (void) ReleaseDC(hwndMain, hdc);\r
8170   if (hwndConsole)\r
8171     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8172 }\r
8173 \r
8174 void\r
8175 DisplayBlackClock(long timeRemaining, int highlight)\r
8176 {\r
8177   HDC hdc;\r
8178   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8179 \r
8180   if(appData.noGUI) return;\r
8181   hdc = GetDC(hwndMain);\r
8182   if (!IsIconic(hwndMain)) {\r
8183     DisplayAClock(hdc, timeRemaining, highlight, \r
8184                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8185   }\r
8186   if (highlight && iconCurrent == iconWhite) {\r
8187     iconCurrent = iconBlack;\r
8188     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8189     if (IsIconic(hwndMain)) {\r
8190       DrawIcon(hdc, 2, 2, iconCurrent);\r
8191     }\r
8192   }\r
8193   (void) ReleaseDC(hwndMain, hdc);\r
8194   if (hwndConsole)\r
8195     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8196 }\r
8197 \r
8198 \r
8199 int\r
8200 LoadGameTimerRunning()\r
8201 {\r
8202   return loadGameTimerEvent != 0;\r
8203 }\r
8204 \r
8205 int\r
8206 StopLoadGameTimer()\r
8207 {\r
8208   if (loadGameTimerEvent == 0) return FALSE;\r
8209   KillTimer(hwndMain, loadGameTimerEvent);\r
8210   loadGameTimerEvent = 0;\r
8211   return TRUE;\r
8212 }\r
8213 \r
8214 void\r
8215 StartLoadGameTimer(long millisec)\r
8216 {\r
8217   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8218                                 (UINT) millisec, NULL);\r
8219 }\r
8220 \r
8221 void\r
8222 AutoSaveGame()\r
8223 {\r
8224   char *defName;\r
8225   FILE *f;\r
8226   char fileTitle[MSG_SIZ];\r
8227 \r
8228   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8229   f = OpenFileDialog(hwndMain, "a", defName,\r
8230                      appData.oldSaveStyle ? "gam" : "pgn",\r
8231                      GAME_FILT, \r
8232                      "Save Game to File", NULL, fileTitle, NULL);\r
8233   if (f != NULL) {\r
8234     SaveGame(f, 0, "");\r
8235     fclose(f);\r
8236   }\r
8237 }\r
8238 \r
8239 \r
8240 void\r
8241 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8242 {\r
8243   if (delayedTimerEvent != 0) {\r
8244     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8245       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8246     }\r
8247     KillTimer(hwndMain, delayedTimerEvent);\r
8248     delayedTimerEvent = 0;\r
8249     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8250     delayedTimerCallback();\r
8251   }\r
8252   delayedTimerCallback = cb;\r
8253   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8254                                 (UINT) millisec, NULL);\r
8255 }\r
8256 \r
8257 DelayedEventCallback\r
8258 GetDelayedEvent()\r
8259 {\r
8260   if (delayedTimerEvent) {\r
8261     return delayedTimerCallback;\r
8262   } else {\r
8263     return NULL;\r
8264   }\r
8265 }\r
8266 \r
8267 void\r
8268 CancelDelayedEvent()\r
8269 {\r
8270   if (delayedTimerEvent) {\r
8271     KillTimer(hwndMain, delayedTimerEvent);\r
8272     delayedTimerEvent = 0;\r
8273   }\r
8274 }\r
8275 \r
8276 DWORD GetWin32Priority(int nice)\r
8277 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8278 /*\r
8279 REALTIME_PRIORITY_CLASS     0x00000100\r
8280 HIGH_PRIORITY_CLASS         0x00000080\r
8281 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8282 NORMAL_PRIORITY_CLASS       0x00000020\r
8283 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8284 IDLE_PRIORITY_CLASS         0x00000040\r
8285 */\r
8286         if (nice < -15) return 0x00000080;\r
8287         if (nice < 0)   return 0x00008000;\r
8288         if (nice == 0)  return 0x00000020;\r
8289         if (nice < 15)  return 0x00004000;\r
8290         return 0x00000040;\r
8291 }\r
8292 \r
8293 /* Start a child process running the given program.\r
8294    The process's standard output can be read from "from", and its\r
8295    standard input can be written to "to".\r
8296    Exit with fatal error if anything goes wrong.\r
8297    Returns an opaque pointer that can be used to destroy the process\r
8298    later.\r
8299 */\r
8300 int\r
8301 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8302 {\r
8303 #define BUFSIZE 4096\r
8304 \r
8305   HANDLE hChildStdinRd, hChildStdinWr,\r
8306     hChildStdoutRd, hChildStdoutWr;\r
8307   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8308   SECURITY_ATTRIBUTES saAttr;\r
8309   BOOL fSuccess;\r
8310   PROCESS_INFORMATION piProcInfo;\r
8311   STARTUPINFO siStartInfo;\r
8312   ChildProc *cp;\r
8313   char buf[MSG_SIZ];\r
8314   DWORD err;\r
8315 \r
8316   if (appData.debugMode) {\r
8317     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8318   }\r
8319 \r
8320   *pr = NoProc;\r
8321 \r
8322   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8323   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8324   saAttr.bInheritHandle = TRUE;\r
8325   saAttr.lpSecurityDescriptor = NULL;\r
8326 \r
8327   /*\r
8328    * The steps for redirecting child's STDOUT:\r
8329    *     1. Create anonymous pipe to be STDOUT for child.\r
8330    *     2. Create a noninheritable duplicate of read handle,\r
8331    *         and close the inheritable read handle.\r
8332    */\r
8333 \r
8334   /* Create a pipe for the child's STDOUT. */\r
8335   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8336     return GetLastError();\r
8337   }\r
8338 \r
8339   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8340   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8341                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8342                              FALSE,     /* not inherited */\r
8343                              DUPLICATE_SAME_ACCESS);\r
8344   if (! fSuccess) {\r
8345     return GetLastError();\r
8346   }\r
8347   CloseHandle(hChildStdoutRd);\r
8348 \r
8349   /*\r
8350    * The steps for redirecting child's STDIN:\r
8351    *     1. Create anonymous pipe to be STDIN for child.\r
8352    *     2. Create a noninheritable duplicate of write handle,\r
8353    *         and close the inheritable write handle.\r
8354    */\r
8355 \r
8356   /* Create a pipe for the child's STDIN. */\r
8357   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8358     return GetLastError();\r
8359   }\r
8360 \r
8361   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8362   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8363                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8364                              FALSE,     /* not inherited */\r
8365                              DUPLICATE_SAME_ACCESS);\r
8366   if (! fSuccess) {\r
8367     return GetLastError();\r
8368   }\r
8369   CloseHandle(hChildStdinWr);\r
8370 \r
8371   /* Arrange to (1) look in dir for the child .exe file, and\r
8372    * (2) have dir be the child's working directory.  Interpret\r
8373    * dir relative to the directory WinBoard loaded from. */\r
8374   GetCurrentDirectory(MSG_SIZ, buf);\r
8375   SetCurrentDirectory(installDir);\r
8376   SetCurrentDirectory(dir);\r
8377 \r
8378   /* Now create the child process. */\r
8379 \r
8380   siStartInfo.cb = sizeof(STARTUPINFO);\r
8381   siStartInfo.lpReserved = NULL;\r
8382   siStartInfo.lpDesktop = NULL;\r
8383   siStartInfo.lpTitle = NULL;\r
8384   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8385   siStartInfo.cbReserved2 = 0;\r
8386   siStartInfo.lpReserved2 = NULL;\r
8387   siStartInfo.hStdInput = hChildStdinRd;\r
8388   siStartInfo.hStdOutput = hChildStdoutWr;\r
8389   siStartInfo.hStdError = hChildStdoutWr;\r
8390 \r
8391   fSuccess = CreateProcess(NULL,\r
8392                            cmdLine,        /* command line */\r
8393                            NULL,           /* process security attributes */\r
8394                            NULL,           /* primary thread security attrs */\r
8395                            TRUE,           /* handles are inherited */\r
8396                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8397                            NULL,           /* use parent's environment */\r
8398                            NULL,\r
8399                            &siStartInfo, /* STARTUPINFO pointer */\r
8400                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8401 \r
8402   err = GetLastError();\r
8403   SetCurrentDirectory(buf); /* return to prev directory */\r
8404   if (! fSuccess) {\r
8405     return err;\r
8406   }\r
8407 \r
8408   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8409     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8410     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8411   }\r
8412 \r
8413   /* Close the handles we don't need in the parent */\r
8414   CloseHandle(piProcInfo.hThread);\r
8415   CloseHandle(hChildStdinRd);\r
8416   CloseHandle(hChildStdoutWr);\r
8417 \r
8418   /* Prepare return value */\r
8419   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8420   cp->kind = CPReal;\r
8421   cp->hProcess = piProcInfo.hProcess;\r
8422   cp->pid = piProcInfo.dwProcessId;\r
8423   cp->hFrom = hChildStdoutRdDup;\r
8424   cp->hTo = hChildStdinWrDup;\r
8425 \r
8426   *pr = (void *) cp;\r
8427 \r
8428   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8429      2000 where engines sometimes don't see the initial command(s)\r
8430      from WinBoard and hang.  I don't understand how that can happen,\r
8431      but the Sleep is harmless, so I've put it in.  Others have also\r
8432      reported what may be the same problem, so hopefully this will fix\r
8433      it for them too.  */\r
8434   Sleep(500);\r
8435 \r
8436   return NO_ERROR;\r
8437 }\r
8438 \r
8439 \r
8440 void\r
8441 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8442 {\r
8443   ChildProc *cp; int result;\r
8444 \r
8445   cp = (ChildProc *) pr;\r
8446   if (cp == NULL) return;\r
8447 \r
8448   switch (cp->kind) {\r
8449   case CPReal:\r
8450     /* TerminateProcess is considered harmful, so... */\r
8451     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8452     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8453     /* The following doesn't work because the chess program\r
8454        doesn't "have the same console" as WinBoard.  Maybe\r
8455        we could arrange for this even though neither WinBoard\r
8456        nor the chess program uses a console for stdio? */\r
8457     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8458 \r
8459     /* [AS] Special termination modes for misbehaving programs... */\r
8460     if( signal == 9 ) { \r
8461         result = TerminateProcess( cp->hProcess, 0 );\r
8462 \r
8463         if ( appData.debugMode) {\r
8464             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8465         }\r
8466     }\r
8467     else if( signal == 10 ) {\r
8468         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8469 \r
8470         if( dw != WAIT_OBJECT_0 ) {\r
8471             result = TerminateProcess( cp->hProcess, 0 );\r
8472 \r
8473             if ( appData.debugMode) {\r
8474                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8475             }\r
8476 \r
8477         }\r
8478     }\r
8479 \r
8480     CloseHandle(cp->hProcess);\r
8481     break;\r
8482 \r
8483   case CPComm:\r
8484     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8485     break;\r
8486 \r
8487   case CPSock:\r
8488     closesocket(cp->sock);\r
8489     WSACleanup();\r
8490     break;\r
8491 \r
8492   case CPRcmd:\r
8493     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8494     closesocket(cp->sock);\r
8495     closesocket(cp->sock2);\r
8496     WSACleanup();\r
8497     break;\r
8498   }\r
8499   free(cp);\r
8500 }\r
8501 \r
8502 void\r
8503 InterruptChildProcess(ProcRef pr)\r
8504 {\r
8505   ChildProc *cp;\r
8506 \r
8507   cp = (ChildProc *) pr;\r
8508   if (cp == NULL) return;\r
8509   switch (cp->kind) {\r
8510   case CPReal:\r
8511     /* The following doesn't work because the chess program\r
8512        doesn't "have the same console" as WinBoard.  Maybe\r
8513        we could arrange for this even though neither WinBoard\r
8514        nor the chess program uses a console for stdio */\r
8515     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8516     break;\r
8517 \r
8518   case CPComm:\r
8519   case CPSock:\r
8520     /* Can't interrupt */\r
8521     break;\r
8522 \r
8523   case CPRcmd:\r
8524     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8525     break;\r
8526   }\r
8527 }\r
8528 \r
8529 \r
8530 int\r
8531 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8532 {\r
8533   char cmdLine[MSG_SIZ];\r
8534 \r
8535   if (port[0] == NULLCHAR) {\r
8536     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8537   } else {\r
8538     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8539   }\r
8540   return StartChildProcess(cmdLine, "", pr);\r
8541 }\r
8542 \r
8543 \r
8544 /* Code to open TCP sockets */\r
8545 \r
8546 int\r
8547 OpenTCP(char *host, char *port, ProcRef *pr)\r
8548 {\r
8549   ChildProc *cp;\r
8550   int err;\r
8551   SOCKET s;\r
8552   struct sockaddr_in sa, mysa;\r
8553   struct hostent FAR *hp;\r
8554   unsigned short uport;\r
8555   WORD wVersionRequested;\r
8556   WSADATA wsaData;\r
8557 \r
8558   /* Initialize socket DLL */\r
8559   wVersionRequested = MAKEWORD(1, 1);\r
8560   err = WSAStartup(wVersionRequested, &wsaData);\r
8561   if (err != 0) return err;\r
8562 \r
8563   /* Make socket */\r
8564   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8565     err = WSAGetLastError();\r
8566     WSACleanup();\r
8567     return err;\r
8568   }\r
8569 \r
8570   /* Bind local address using (mostly) don't-care values.\r
8571    */\r
8572   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8573   mysa.sin_family = AF_INET;\r
8574   mysa.sin_addr.s_addr = INADDR_ANY;\r
8575   uport = (unsigned short) 0;\r
8576   mysa.sin_port = htons(uport);\r
8577   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8578       == SOCKET_ERROR) {\r
8579     err = WSAGetLastError();\r
8580     WSACleanup();\r
8581     return err;\r
8582   }\r
8583 \r
8584   /* Resolve remote host name */\r
8585   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8586   if (!(hp = gethostbyname(host))) {\r
8587     unsigned int b0, b1, b2, b3;\r
8588 \r
8589     err = WSAGetLastError();\r
8590 \r
8591     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8592       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8593       hp->h_addrtype = AF_INET;\r
8594       hp->h_length = 4;\r
8595       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8596       hp->h_addr_list[0] = (char *) malloc(4);\r
8597       hp->h_addr_list[0][0] = (char) b0;\r
8598       hp->h_addr_list[0][1] = (char) b1;\r
8599       hp->h_addr_list[0][2] = (char) b2;\r
8600       hp->h_addr_list[0][3] = (char) b3;\r
8601     } else {\r
8602       WSACleanup();\r
8603       return err;\r
8604     }\r
8605   }\r
8606   sa.sin_family = hp->h_addrtype;\r
8607   uport = (unsigned short) atoi(port);\r
8608   sa.sin_port = htons(uport);\r
8609   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8610 \r
8611   /* Make connection */\r
8612   if (connect(s, (struct sockaddr *) &sa,\r
8613               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8614     err = WSAGetLastError();\r
8615     WSACleanup();\r
8616     return err;\r
8617   }\r
8618 \r
8619   /* Prepare return value */\r
8620   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8621   cp->kind = CPSock;\r
8622   cp->sock = s;\r
8623   *pr = (ProcRef *) cp;\r
8624 \r
8625   return NO_ERROR;\r
8626 }\r
8627 \r
8628 int\r
8629 OpenCommPort(char *name, ProcRef *pr)\r
8630 {\r
8631   HANDLE h;\r
8632   COMMTIMEOUTS ct;\r
8633   ChildProc *cp;\r
8634   char fullname[MSG_SIZ];\r
8635 \r
8636   if (*name != '\\')\r
8637     sprintf(fullname, "\\\\.\\%s", name);\r
8638   else\r
8639     strcpy(fullname, name);\r
8640 \r
8641   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8642                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8643   if (h == (HANDLE) -1) {\r
8644     return GetLastError();\r
8645   }\r
8646   hCommPort = h;\r
8647 \r
8648   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8649 \r
8650   /* Accumulate characters until a 100ms pause, then parse */\r
8651   ct.ReadIntervalTimeout = 100;\r
8652   ct.ReadTotalTimeoutMultiplier = 0;\r
8653   ct.ReadTotalTimeoutConstant = 0;\r
8654   ct.WriteTotalTimeoutMultiplier = 0;\r
8655   ct.WriteTotalTimeoutConstant = 0;\r
8656   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8657 \r
8658   /* Prepare return value */\r
8659   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8660   cp->kind = CPComm;\r
8661   cp->hFrom = h;\r
8662   cp->hTo = h;\r
8663   *pr = (ProcRef *) cp;\r
8664 \r
8665   return NO_ERROR;\r
8666 }\r
8667 \r
8668 int\r
8669 OpenLoopback(ProcRef *pr)\r
8670 {\r
8671   DisplayFatalError("Not implemented", 0, 1);\r
8672   return NO_ERROR;\r
8673 }\r
8674 \r
8675 \r
8676 int\r
8677 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8678 {\r
8679   ChildProc *cp;\r
8680   int err;\r
8681   SOCKET s, s2, s3;\r
8682   struct sockaddr_in sa, mysa;\r
8683   struct hostent FAR *hp;\r
8684   unsigned short uport;\r
8685   WORD wVersionRequested;\r
8686   WSADATA wsaData;\r
8687   int fromPort;\r
8688   char stderrPortStr[MSG_SIZ];\r
8689 \r
8690   /* Initialize socket DLL */\r
8691   wVersionRequested = MAKEWORD(1, 1);\r
8692   err = WSAStartup(wVersionRequested, &wsaData);\r
8693   if (err != 0) return err;\r
8694 \r
8695   /* Resolve remote host name */\r
8696   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8697   if (!(hp = gethostbyname(host))) {\r
8698     unsigned int b0, b1, b2, b3;\r
8699 \r
8700     err = WSAGetLastError();\r
8701 \r
8702     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8703       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8704       hp->h_addrtype = AF_INET;\r
8705       hp->h_length = 4;\r
8706       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8707       hp->h_addr_list[0] = (char *) malloc(4);\r
8708       hp->h_addr_list[0][0] = (char) b0;\r
8709       hp->h_addr_list[0][1] = (char) b1;\r
8710       hp->h_addr_list[0][2] = (char) b2;\r
8711       hp->h_addr_list[0][3] = (char) b3;\r
8712     } else {\r
8713       WSACleanup();\r
8714       return err;\r
8715     }\r
8716   }\r
8717   sa.sin_family = hp->h_addrtype;\r
8718   uport = (unsigned short) 514;\r
8719   sa.sin_port = htons(uport);\r
8720   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8721 \r
8722   /* Bind local socket to unused "privileged" port address\r
8723    */\r
8724   s = INVALID_SOCKET;\r
8725   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8726   mysa.sin_family = AF_INET;\r
8727   mysa.sin_addr.s_addr = INADDR_ANY;\r
8728   for (fromPort = 1023;; fromPort--) {\r
8729     if (fromPort < 0) {\r
8730       WSACleanup();\r
8731       return WSAEADDRINUSE;\r
8732     }\r
8733     if (s == INVALID_SOCKET) {\r
8734       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8735         err = WSAGetLastError();\r
8736         WSACleanup();\r
8737         return err;\r
8738       }\r
8739     }\r
8740     uport = (unsigned short) fromPort;\r
8741     mysa.sin_port = htons(uport);\r
8742     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8743         == SOCKET_ERROR) {\r
8744       err = WSAGetLastError();\r
8745       if (err == WSAEADDRINUSE) continue;\r
8746       WSACleanup();\r
8747       return err;\r
8748     }\r
8749     if (connect(s, (struct sockaddr *) &sa,\r
8750       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8751       err = WSAGetLastError();\r
8752       if (err == WSAEADDRINUSE) {\r
8753         closesocket(s);\r
8754         s = -1;\r
8755         continue;\r
8756       }\r
8757       WSACleanup();\r
8758       return err;\r
8759     }\r
8760     break;\r
8761   }\r
8762 \r
8763   /* Bind stderr local socket to unused "privileged" port address\r
8764    */\r
8765   s2 = INVALID_SOCKET;\r
8766   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8767   mysa.sin_family = AF_INET;\r
8768   mysa.sin_addr.s_addr = INADDR_ANY;\r
8769   for (fromPort = 1023;; fromPort--) {\r
8770     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8771     if (fromPort < 0) {\r
8772       (void) closesocket(s);\r
8773       WSACleanup();\r
8774       return WSAEADDRINUSE;\r
8775     }\r
8776     if (s2 == INVALID_SOCKET) {\r
8777       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8778         err = WSAGetLastError();\r
8779         closesocket(s);\r
8780         WSACleanup();\r
8781         return err;\r
8782       }\r
8783     }\r
8784     uport = (unsigned short) fromPort;\r
8785     mysa.sin_port = htons(uport);\r
8786     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8787         == SOCKET_ERROR) {\r
8788       err = WSAGetLastError();\r
8789       if (err == WSAEADDRINUSE) continue;\r
8790       (void) closesocket(s);\r
8791       WSACleanup();\r
8792       return err;\r
8793     }\r
8794     if (listen(s2, 1) == SOCKET_ERROR) {\r
8795       err = WSAGetLastError();\r
8796       if (err == WSAEADDRINUSE) {\r
8797         closesocket(s2);\r
8798         s2 = INVALID_SOCKET;\r
8799         continue;\r
8800       }\r
8801       (void) closesocket(s);\r
8802       (void) closesocket(s2);\r
8803       WSACleanup();\r
8804       return err;\r
8805     }\r
8806     break;\r
8807   }\r
8808   prevStderrPort = fromPort; // remember port used\r
8809   sprintf(stderrPortStr, "%d", fromPort);\r
8810 \r
8811   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
8812     err = WSAGetLastError();\r
8813     (void) closesocket(s);\r
8814     (void) closesocket(s2);\r
8815     WSACleanup();\r
8816     return err;\r
8817   }\r
8818 \r
8819   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
8820     err = WSAGetLastError();\r
8821     (void) closesocket(s);\r
8822     (void) closesocket(s2);\r
8823     WSACleanup();\r
8824     return err;\r
8825   }\r
8826   if (*user == NULLCHAR) user = UserName();\r
8827   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
8828     err = WSAGetLastError();\r
8829     (void) closesocket(s);\r
8830     (void) closesocket(s2);\r
8831     WSACleanup();\r
8832     return err;\r
8833   }\r
8834   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
8835     err = WSAGetLastError();\r
8836     (void) closesocket(s);\r
8837     (void) closesocket(s2);\r
8838     WSACleanup();\r
8839     return err;\r
8840   }\r
8841 \r
8842   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
8843     err = WSAGetLastError();\r
8844     (void) closesocket(s);\r
8845     (void) closesocket(s2);\r
8846     WSACleanup();\r
8847     return err;\r
8848   }\r
8849   (void) closesocket(s2);  /* Stop listening */\r
8850 \r
8851   /* Prepare return value */\r
8852   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8853   cp->kind = CPRcmd;\r
8854   cp->sock = s;\r
8855   cp->sock2 = s3;\r
8856   *pr = (ProcRef *) cp;\r
8857 \r
8858   return NO_ERROR;\r
8859 }\r
8860 \r
8861 \r
8862 InputSourceRef\r
8863 AddInputSource(ProcRef pr, int lineByLine,\r
8864                InputCallback func, VOIDSTAR closure)\r
8865 {\r
8866   InputSource *is, *is2 = NULL;\r
8867   ChildProc *cp = (ChildProc *) pr;\r
8868 \r
8869   is = (InputSource *) calloc(1, sizeof(InputSource));\r
8870   is->lineByLine = lineByLine;\r
8871   is->func = func;\r
8872   is->closure = closure;\r
8873   is->second = NULL;\r
8874   is->next = is->buf;\r
8875   if (pr == NoProc) {\r
8876     is->kind = CPReal;\r
8877     consoleInputSource = is;\r
8878   } else {\r
8879     is->kind = cp->kind;\r
8880     /* \r
8881         [AS] Try to avoid a race condition if the thread is given control too early:\r
8882         we create all threads suspended so that the is->hThread variable can be\r
8883         safely assigned, then let the threads start with ResumeThread.\r
8884     */\r
8885     switch (cp->kind) {\r
8886     case CPReal:\r
8887       is->hFile = cp->hFrom;\r
8888       cp->hFrom = NULL; /* now owned by InputThread */\r
8889       is->hThread =\r
8890         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
8891                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
8892       break;\r
8893 \r
8894     case CPComm:\r
8895       is->hFile = cp->hFrom;\r
8896       cp->hFrom = NULL; /* now owned by InputThread */\r
8897       is->hThread =\r
8898         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
8899                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
8900       break;\r
8901 \r
8902     case CPSock:\r
8903       is->sock = cp->sock;\r
8904       is->hThread =\r
8905         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
8906                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
8907       break;\r
8908 \r
8909     case CPRcmd:\r
8910       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
8911       *is2 = *is;\r
8912       is->sock = cp->sock;\r
8913       is->second = is2;\r
8914       is2->sock = cp->sock2;\r
8915       is2->second = is2;\r
8916       is->hThread =\r
8917         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
8918                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
8919       is2->hThread =\r
8920         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
8921                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
8922       break;\r
8923     }\r
8924 \r
8925     if( is->hThread != NULL ) {\r
8926         ResumeThread( is->hThread );\r
8927     }\r
8928 \r
8929     if( is2 != NULL && is2->hThread != NULL ) {\r
8930         ResumeThread( is2->hThread );\r
8931     }\r
8932   }\r
8933 \r
8934   return (InputSourceRef) is;\r
8935 }\r
8936 \r
8937 void\r
8938 RemoveInputSource(InputSourceRef isr)\r
8939 {\r
8940   InputSource *is;\r
8941 \r
8942   is = (InputSource *) isr;\r
8943   is->hThread = NULL;  /* tell thread to stop */\r
8944   CloseHandle(is->hThread);\r
8945   if (is->second != NULL) {\r
8946     is->second->hThread = NULL;\r
8947     CloseHandle(is->second->hThread);\r
8948   }\r
8949 }\r
8950 \r
8951 int no_wrap(char *message, int count)\r
8952 {\r
8953     ConsoleOutput(message, count, FALSE);\r
8954     return count;\r
8955 }\r
8956 \r
8957 int\r
8958 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
8959 {\r
8960   DWORD dOutCount;\r
8961   int outCount = SOCKET_ERROR;\r
8962   ChildProc *cp = (ChildProc *) pr;\r
8963   static OVERLAPPED ovl;\r
8964   static int line = 0;\r
8965 \r
8966   if (pr == NoProc)\r
8967   {\r
8968     if (appData.noJoin || !appData.useInternalWrap)\r
8969       return no_wrap(message, count);\r
8970     else\r
8971     {\r
8972       int width = get_term_width();\r
8973       int len = wrap(NULL, message, count, width, &line);\r
8974       char *msg = malloc(len);\r
8975       int dbgchk;\r
8976 \r
8977       if (!msg)\r
8978         return no_wrap(message, count);\r
8979       else\r
8980       {\r
8981         dbgchk = wrap(msg, message, count, width, &line);\r
8982         if (dbgchk != len && appData.debugMode)\r
8983             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
8984         ConsoleOutput(msg, len, FALSE);\r
8985         free(msg);\r
8986         return len;\r
8987       }\r
8988     }\r
8989   }\r
8990 \r
8991   if (ovl.hEvent == NULL) {\r
8992     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8993   }\r
8994   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8995 \r
8996   switch (cp->kind) {\r
8997   case CPSock:\r
8998   case CPRcmd:\r
8999     outCount = send(cp->sock, message, count, 0);\r
9000     if (outCount == SOCKET_ERROR) {\r
9001       *outError = WSAGetLastError();\r
9002     } else {\r
9003       *outError = NO_ERROR;\r
9004     }\r
9005     break;\r
9006 \r
9007   case CPReal:\r
9008     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9009                   &dOutCount, NULL)) {\r
9010       *outError = NO_ERROR;\r
9011       outCount = (int) dOutCount;\r
9012     } else {\r
9013       *outError = GetLastError();\r
9014     }\r
9015     break;\r
9016 \r
9017   case CPComm:\r
9018     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9019                             &dOutCount, &ovl);\r
9020     if (*outError == NO_ERROR) {\r
9021       outCount = (int) dOutCount;\r
9022     }\r
9023     break;\r
9024   }\r
9025   return outCount;\r
9026 }\r
9027 \r
9028 int\r
9029 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9030                        long msdelay)\r
9031 {\r
9032   /* Ignore delay, not implemented for WinBoard */\r
9033   return OutputToProcess(pr, message, count, outError);\r
9034 }\r
9035 \r
9036 \r
9037 void\r
9038 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9039                         char *buf, int count, int error)\r
9040 {\r
9041   DisplayFatalError("Not implemented", 0, 1);\r
9042 }\r
9043 \r
9044 /* see wgamelist.c for Game List functions */\r
9045 /* see wedittags.c for Edit Tags functions */\r
9046 \r
9047 \r
9048 VOID\r
9049 ICSInitScript()\r
9050 {\r
9051   FILE *f;\r
9052   char buf[MSG_SIZ];\r
9053   char *dummy;\r
9054 \r
9055   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9056     f = fopen(buf, "r");\r
9057     if (f != NULL) {\r
9058       ProcessICSInitScript(f);\r
9059       fclose(f);\r
9060     }\r
9061   }\r
9062 }\r
9063 \r
9064 \r
9065 VOID\r
9066 StartAnalysisClock()\r
9067 {\r
9068   if (analysisTimerEvent) return;\r
9069   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9070                                         (UINT) 2000, NULL);\r
9071 }\r
9072 \r
9073 VOID\r
9074 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9075 {\r
9076   highlightInfo.sq[0].x = fromX;\r
9077   highlightInfo.sq[0].y = fromY;\r
9078   highlightInfo.sq[1].x = toX;\r
9079   highlightInfo.sq[1].y = toY;\r
9080 }\r
9081 \r
9082 VOID\r
9083 ClearHighlights()\r
9084 {\r
9085   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9086     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9087 }\r
9088 \r
9089 VOID\r
9090 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9091 {\r
9092   premoveHighlightInfo.sq[0].x = fromX;\r
9093   premoveHighlightInfo.sq[0].y = fromY;\r
9094   premoveHighlightInfo.sq[1].x = toX;\r
9095   premoveHighlightInfo.sq[1].y = toY;\r
9096 }\r
9097 \r
9098 VOID\r
9099 ClearPremoveHighlights()\r
9100 {\r
9101   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9102     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9103 }\r
9104 \r
9105 VOID\r
9106 ShutDownFrontEnd()\r
9107 {\r
9108   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9109   DeleteClipboardTempFiles();\r
9110 }\r
9111 \r
9112 void\r
9113 BoardToTop()\r
9114 {\r
9115     if (IsIconic(hwndMain))\r
9116       ShowWindow(hwndMain, SW_RESTORE);\r
9117 \r
9118     SetActiveWindow(hwndMain);\r
9119 }\r
9120 \r
9121 /*\r
9122  * Prototypes for animation support routines\r
9123  */\r
9124 static void ScreenSquare(int column, int row, POINT * pt);\r
9125 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9126      POINT frames[], int * nFrames);\r
9127 \r
9128 \r
9129 void\r
9130 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9131 {       // [HGM] atomic: animate blast wave\r
9132         int i;\r
9133 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9134         explodeInfo.fromX = fromX;\r
9135         explodeInfo.fromY = fromY;\r
9136         explodeInfo.toX = toX;\r
9137         explodeInfo.toY = toY;\r
9138         for(i=1; i<nFrames; i++) {\r
9139             explodeInfo.radius = (i*180)/(nFrames-1);\r
9140             DrawPosition(FALSE, NULL);\r
9141             Sleep(appData.animSpeed);\r
9142         }\r
9143         explodeInfo.radius = 0;\r
9144         DrawPosition(TRUE, NULL);\r
9145 }\r
9146 \r
9147 #define kFactor 4\r
9148 \r
9149 void\r
9150 AnimateMove(board, fromX, fromY, toX, toY)\r
9151      Board board;\r
9152      int fromX;\r
9153      int fromY;\r
9154      int toX;\r
9155      int toY;\r
9156 {\r
9157   ChessSquare piece;\r
9158   POINT start, finish, mid;\r
9159   POINT frames[kFactor * 2 + 1];\r
9160   int nFrames, n;\r
9161 \r
9162   if (!appData.animate) return;\r
9163   if (doingSizing) return;\r
9164   if (fromY < 0 || fromX < 0) return;\r
9165   piece = board[fromY][fromX];\r
9166   if (piece >= EmptySquare) return;\r
9167 \r
9168   ScreenSquare(fromX, fromY, &start);\r
9169   ScreenSquare(toX, toY, &finish);\r
9170 \r
9171   /* All pieces except knights move in straight line */\r
9172   if (piece != WhiteKnight && piece != BlackKnight) {\r
9173     mid.x = start.x + (finish.x - start.x) / 2;\r
9174     mid.y = start.y + (finish.y - start.y) / 2;\r
9175   } else {\r
9176     /* Knight: make diagonal movement then straight */\r
9177     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9178        mid.x = start.x + (finish.x - start.x) / 2;\r
9179        mid.y = finish.y;\r
9180      } else {\r
9181        mid.x = finish.x;\r
9182        mid.y = start.y + (finish.y - start.y) / 2;\r
9183      }\r
9184   }\r
9185   \r
9186   /* Don't use as many frames for very short moves */\r
9187   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9188     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9189   else\r
9190     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9191 \r
9192   animInfo.from.x = fromX;\r
9193   animInfo.from.y = fromY;\r
9194   animInfo.to.x = toX;\r
9195   animInfo.to.y = toY;\r
9196   animInfo.lastpos = start;\r
9197   animInfo.piece = piece;\r
9198   for (n = 0; n < nFrames; n++) {\r
9199     animInfo.pos = frames[n];\r
9200     DrawPosition(FALSE, NULL);\r
9201     animInfo.lastpos = animInfo.pos;\r
9202     Sleep(appData.animSpeed);\r
9203   }\r
9204   animInfo.pos = finish;\r
9205   DrawPosition(FALSE, NULL);\r
9206   animInfo.piece = EmptySquare;\r
9207   if(gameInfo.variant == VariantAtomic && \r
9208      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9209         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9210 }\r
9211 \r
9212 /*      Convert board position to corner of screen rect and color       */\r
9213 \r
9214 static void\r
9215 ScreenSquare(column, row, pt)\r
9216      int column; int row; POINT * pt;\r
9217 {\r
9218   if (flipView) {\r
9219     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9220     pt->y = lineGap + row * (squareSize + lineGap);\r
9221   } else {\r
9222     pt->x = lineGap + column * (squareSize + lineGap);\r
9223     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9224   }\r
9225 }\r
9226 \r
9227 /*      Generate a series of frame coords from start->mid->finish.\r
9228         The movement rate doubles until the half way point is\r
9229         reached, then halves back down to the final destination,\r
9230         which gives a nice slow in/out effect. The algorithmn\r
9231         may seem to generate too many intermediates for short\r
9232         moves, but remember that the purpose is to attract the\r
9233         viewers attention to the piece about to be moved and\r
9234         then to where it ends up. Too few frames would be less\r
9235         noticeable.                                             */\r
9236 \r
9237 static void\r
9238 Tween(start, mid, finish, factor, frames, nFrames)\r
9239      POINT * start; POINT * mid;\r
9240      POINT * finish; int factor;\r
9241      POINT frames[]; int * nFrames;\r
9242 {\r
9243   int n, fraction = 1, count = 0;\r
9244 \r
9245   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9246   for (n = 0; n < factor; n++)\r
9247     fraction *= 2;\r
9248   for (n = 0; n < factor; n++) {\r
9249     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9250     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9251     count ++;\r
9252     fraction = fraction / 2;\r
9253   }\r
9254   \r
9255   /* Midpoint */\r
9256   frames[count] = *mid;\r
9257   count ++;\r
9258   \r
9259   /* Slow out, stepping 1/2, then 1/4, ... */\r
9260   fraction = 2;\r
9261   for (n = 0; n < factor; n++) {\r
9262     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9263     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9264     count ++;\r
9265     fraction = fraction * 2;\r
9266   }\r
9267   *nFrames = count;\r
9268 }\r
9269 \r
9270 void\r
9271 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9272 {\r
9273     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9274 \r
9275     EvalGraphSet( first, last, current, pvInfoList );\r
9276 }\r