Indicate squares a lifted piece can legally move to
[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 \r
2010   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2011   if (appData.showButtonBar) {\r
2012     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2013       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2014   } else {\r
2015     messageRect.right = OUTER_MARGIN + boardWidth;\r
2016   }\r
2017   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2018   messageRect.bottom = messageRect.top + messageSize.cy;\r
2019 \r
2020   boardRect.left = OUTER_MARGIN;\r
2021   boardRect.right = boardRect.left + boardWidth;\r
2022   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2023   boardRect.bottom = boardRect.top + boardHeight;\r
2024 \r
2025   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2026   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2027   oldBoardSize = boardSize;\r
2028   oldTinyLayout = tinyLayout;\r
2029   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2030   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2031     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2032   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2033   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2034   wpMain.height = winH; //       without disturbing window attachments\r
2035   GetWindowRect(hwndMain, &wrect);\r
2036   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2037                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2038 \r
2039   // [HGM] placement: let attached windows follow size change.\r
2040   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2041   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2042   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2043   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2044   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2045 \r
2046   /* compensate if menu bar wrapped */\r
2047   GetClientRect(hwndMain, &crect);\r
2048   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2049   wpMain.height += offby;\r
2050   switch (flags) {\r
2051   case WMSZ_TOPLEFT:\r
2052     SetWindowPos(hwndMain, NULL, \r
2053                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2054                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2055     break;\r
2056 \r
2057   case WMSZ_TOPRIGHT:\r
2058   case WMSZ_TOP:\r
2059     SetWindowPos(hwndMain, NULL, \r
2060                  wrect.left, wrect.bottom - wpMain.height, \r
2061                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2062     break;\r
2063 \r
2064   case WMSZ_BOTTOMLEFT:\r
2065   case WMSZ_LEFT:\r
2066     SetWindowPos(hwndMain, NULL, \r
2067                  wrect.right - wpMain.width, wrect.top, \r
2068                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2069     break;\r
2070 \r
2071   case WMSZ_BOTTOMRIGHT:\r
2072   case WMSZ_BOTTOM:\r
2073   case WMSZ_RIGHT:\r
2074   default:\r
2075     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2076                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2077     break;\r
2078   }\r
2079 \r
2080   hwndPause = NULL;\r
2081   for (i = 0; i < N_BUTTONS; i++) {\r
2082     if (buttonDesc[i].hwnd != NULL) {\r
2083       DestroyWindow(buttonDesc[i].hwnd);\r
2084       buttonDesc[i].hwnd = NULL;\r
2085     }\r
2086     if (appData.showButtonBar) {\r
2087       buttonDesc[i].hwnd =\r
2088         CreateWindow("BUTTON", buttonDesc[i].label,\r
2089                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2090                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2091                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2092                      (HMENU) buttonDesc[i].id,\r
2093                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2094       if (tinyLayout) {\r
2095         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2096                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2097                     MAKELPARAM(FALSE, 0));\r
2098       }\r
2099       if (buttonDesc[i].id == IDM_Pause)\r
2100         hwndPause = buttonDesc[i].hwnd;\r
2101       buttonDesc[i].wndproc = (WNDPROC)\r
2102         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2103     }\r
2104   }\r
2105   if (gridPen != NULL) DeleteObject(gridPen);\r
2106   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2107   if (premovePen != NULL) DeleteObject(premovePen);\r
2108   if (lineGap != 0) {\r
2109     logbrush.lbStyle = BS_SOLID;\r
2110     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2111     gridPen =\r
2112       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2113                    lineGap, &logbrush, 0, NULL);\r
2114     logbrush.lbColor = highlightSquareColor;\r
2115     highlightPen =\r
2116       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2117                    lineGap, &logbrush, 0, NULL);\r
2118 \r
2119     logbrush.lbColor = premoveHighlightColor; \r
2120     premovePen =\r
2121       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2122                    lineGap, &logbrush, 0, NULL);\r
2123 \r
2124     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2125     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2126       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2127       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2128         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2129       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2130         BOARD_WIDTH * (squareSize + lineGap);\r
2131       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2132     }\r
2133     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2134       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2135       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2136         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2137         lineGap / 2 + (i * (squareSize + lineGap));\r
2138       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2139         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2140       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2141     }\r
2142   }\r
2143 \r
2144   /* [HGM] Licensing requirement */\r
2145 #ifdef GOTHIC\r
2146   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2147 #endif\r
2148 #ifdef FALCON\r
2149   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2150 #endif\r
2151   GothicPopUp( "", VariantNormal);\r
2152 \r
2153 \r
2154 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2155 \r
2156   /* Load piece bitmaps for this board size */\r
2157   for (i=0; i<=2; i++) {\r
2158     for (piece = WhitePawn;\r
2159          (int) piece < (int) BlackPawn;\r
2160          piece = (ChessSquare) ((int) piece + 1)) {\r
2161       if (pieceBitmap[i][piece] != NULL)\r
2162         DeleteObject(pieceBitmap[i][piece]);\r
2163     }\r
2164   }\r
2165 \r
2166   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2167   // Orthodox Chess pieces\r
2168   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2169   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2170   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2171   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2172   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2173   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2174   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2175   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2176   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2177   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2178   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2179   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2180   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2181   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2182   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2183   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2184     // in Shogi, Hijack the unused Queen for Lance\r
2185     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2186     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2187     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2188   } else {\r
2189     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2190     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2191     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2192   }\r
2193 \r
2194   if(squareSize <= 72 && squareSize >= 33) { \r
2195     /* A & C are available in most sizes now */\r
2196     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2197       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2198       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2199       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2200       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2201       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2202       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2203       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2204       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2205       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2206       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2207       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2208       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2209     } else { // Smirf-like\r
2210       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2211       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2212       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2213     }\r
2214     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2215       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2216       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2217       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2218     } else { // WinBoard standard\r
2219       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2220       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2221       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2222     }\r
2223   }\r
2224 \r
2225 \r
2226   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2227     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2228     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2229     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2230     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2231     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2232     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2233     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2234     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2235     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2236     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2237     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2238     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2239     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2240     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2241     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2242     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2243     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2244     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2245     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2246     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2247     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2248     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2249     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2250     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2251     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2252     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2253     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2254     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2255     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2256     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2257 \r
2258     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2259       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2260       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2261       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2262       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2263       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2264       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2265       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2266       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2267       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2268       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2269       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2270       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2271     } else {\r
2272       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2273       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2274       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2275       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2276       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2277       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2278       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2279       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2280       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2281       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2282       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2283       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2284     }\r
2285 \r
2286   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2287     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2288     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2289     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2290     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2291     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2292     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2293     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2294     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2295     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2296     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2297     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2298     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2299     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2300     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2301   }\r
2302 \r
2303 \r
2304   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2305   /* special Shogi support in this size */\r
2306   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2307       for (piece = WhitePawn;\r
2308            (int) piece < (int) BlackPawn;\r
2309            piece = (ChessSquare) ((int) piece + 1)) {\r
2310         if (pieceBitmap[i][piece] != NULL)\r
2311           DeleteObject(pieceBitmap[i][piece]);\r
2312       }\r
2313     }\r
2314   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2315   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2316   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2317   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2318   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2319   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2320   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2321   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2322   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2323   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2324   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2325   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2326   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2327   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2328   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2329   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2330   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2331   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2332   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2333   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2334   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2335   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2336   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2337   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2338   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2339   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2340   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2341   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2342   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2343   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2344   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2345   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2346   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2347   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2348   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2349   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2350   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2351   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2352   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2353   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2354   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2355   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2356   minorSize = 0;\r
2357   }\r
2358 }\r
2359 \r
2360 HBITMAP\r
2361 PieceBitmap(ChessSquare p, int kind)\r
2362 {\r
2363   if ((int) p >= (int) BlackPawn)\r
2364     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2365 \r
2366   return pieceBitmap[kind][(int) p];\r
2367 }\r
2368 \r
2369 /***************************************************************/\r
2370 \r
2371 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2372 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2373 /*\r
2374 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2375 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2376 */\r
2377 \r
2378 VOID\r
2379 SquareToPos(int row, int column, int * x, int * y)\r
2380 {\r
2381   if (flipView) {\r
2382     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2383     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2384   } else {\r
2385     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2386     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2387   }\r
2388 }\r
2389 \r
2390 VOID\r
2391 DrawCoordsOnDC(HDC hdc)\r
2392 {\r
2393   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
2394   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
2395   char str[2] = { NULLCHAR, NULLCHAR };\r
2396   int oldMode, oldAlign, x, y, start, i;\r
2397   HFONT oldFont;\r
2398   HBRUSH oldBrush;\r
2399 \r
2400   if (!appData.showCoords)\r
2401     return;\r
2402 \r
2403   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2404 \r
2405   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2406   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2407   oldAlign = GetTextAlign(hdc);\r
2408   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2409 \r
2410   y = boardRect.top + lineGap;\r
2411   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2412 \r
2413   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2414   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2415     str[0] = files[start + i];\r
2416     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2417     y += squareSize + lineGap;\r
2418   }\r
2419 \r
2420   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2421 \r
2422   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2423   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2424     str[0] = ranks[start + i];\r
2425     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2426     x += squareSize + lineGap;\r
2427   }    \r
2428 \r
2429   SelectObject(hdc, oldBrush);\r
2430   SetBkMode(hdc, oldMode);\r
2431   SetTextAlign(hdc, oldAlign);\r
2432   SelectObject(hdc, oldFont);\r
2433 }\r
2434 \r
2435 VOID\r
2436 DrawGridOnDC(HDC hdc)\r
2437 {\r
2438   HPEN oldPen;\r
2439  \r
2440   if (lineGap != 0) {\r
2441     oldPen = SelectObject(hdc, gridPen);\r
2442     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2443     SelectObject(hdc, oldPen);\r
2444   }\r
2445 }\r
2446 \r
2447 #define HIGHLIGHT_PEN 0\r
2448 #define PREMOVE_PEN   1\r
2449 \r
2450 VOID\r
2451 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2452 {\r
2453   int x1, y1;\r
2454   HPEN oldPen, hPen;\r
2455   if (lineGap == 0) return;\r
2456   if (flipView) {\r
2457     x1 = boardRect.left +\r
2458       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2459     y1 = boardRect.top +\r
2460       lineGap/2 + y * (squareSize + lineGap);\r
2461   } else {\r
2462     x1 = boardRect.left +\r
2463       lineGap/2 + x * (squareSize + lineGap);\r
2464     y1 = boardRect.top +\r
2465       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2466   }\r
2467   hPen = pen ? premovePen : highlightPen;\r
2468   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2469   MoveToEx(hdc, x1, y1, NULL);\r
2470   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2471   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2472   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2473   LineTo(hdc, x1, y1);\r
2474   SelectObject(hdc, oldPen);\r
2475 }\r
2476 \r
2477 VOID\r
2478 DrawHighlightsOnDC(HDC hdc)\r
2479 {\r
2480   int i;\r
2481   for (i=0; i<2; i++) {\r
2482     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2483       DrawHighlightOnDC(hdc, TRUE,\r
2484                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2485                         HIGHLIGHT_PEN);\r
2486   }\r
2487   for (i=0; i<2; i++) {\r
2488     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2489         premoveHighlightInfo.sq[i].y >= 0) {\r
2490         DrawHighlightOnDC(hdc, TRUE,\r
2491                           premoveHighlightInfo.sq[i].x, \r
2492                           premoveHighlightInfo.sq[i].y,\r
2493                           PREMOVE_PEN);\r
2494     }\r
2495   }\r
2496 }\r
2497 \r
2498 /* Note: sqcolor is used only in monoMode */\r
2499 /* Note that this code is largely duplicated in woptions.c,\r
2500    function DrawSampleSquare, so that needs to be updated too */\r
2501 VOID\r
2502 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2503 {\r
2504   HBITMAP oldBitmap;\r
2505   HBRUSH oldBrush;\r
2506   int tmpSize;\r
2507 \r
2508   if (appData.blindfold) return;\r
2509 \r
2510   /* [AS] Use font-based pieces if needed */\r
2511   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
2512     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2513     CreatePiecesFromFont();\r
2514 \r
2515     if( fontBitmapSquareSize == squareSize ) {\r
2516         int index = TranslatePieceToFontPiece(piece);\r
2517 \r
2518         SelectObject( tmphdc, hPieceMask[ index ] );\r
2519 \r
2520         BitBlt( hdc,\r
2521             x, y,\r
2522             squareSize, squareSize,\r
2523             tmphdc,\r
2524             0, 0,\r
2525             SRCAND );\r
2526 \r
2527         SelectObject( tmphdc, hPieceFace[ index ] );\r
2528 \r
2529         BitBlt( hdc,\r
2530             x, y,\r
2531             squareSize, squareSize,\r
2532             tmphdc,\r
2533             0, 0,\r
2534             SRCPAINT );\r
2535 \r
2536         return;\r
2537     }\r
2538   }\r
2539 \r
2540   if (appData.monoMode) {\r
2541     SelectObject(tmphdc, PieceBitmap(piece, \r
2542       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2543     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2544            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2545   } else {\r
2546     tmpSize = squareSize;\r
2547     if(minorSize &&\r
2548         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2549          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2550       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2551       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2552       x += (squareSize - minorSize)>>1;\r
2553       y += squareSize - minorSize - 2;\r
2554       tmpSize = minorSize;\r
2555     }\r
2556     if (color || appData.allWhite ) {\r
2557       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2558       if( color )\r
2559               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2560       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2561       if(appData.upsideDown && color==flipView)\r
2562         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2563       else\r
2564         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2565       /* Use black for outline of white pieces */\r
2566       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2567       if(appData.upsideDown && color==flipView)\r
2568         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2569       else\r
2570         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2571     } else {\r
2572       /* Use square color for details of black pieces */\r
2573       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2574       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2575       if(appData.upsideDown && !flipView)\r
2576         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2577       else\r
2578         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2579     }\r
2580     SelectObject(hdc, oldBrush);\r
2581     SelectObject(tmphdc, oldBitmap);\r
2582   }\r
2583 }\r
2584 \r
2585 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2586 int GetBackTextureMode( int algo )\r
2587 {\r
2588     int result = BACK_TEXTURE_MODE_DISABLED;\r
2589 \r
2590     switch( algo ) \r
2591     {\r
2592         case BACK_TEXTURE_MODE_PLAIN:\r
2593             result = 1; /* Always use identity map */\r
2594             break;\r
2595         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2596             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2597             break;\r
2598     }\r
2599 \r
2600     return result;\r
2601 }\r
2602 \r
2603 /* \r
2604     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2605     to handle redraws cleanly (as random numbers would always be different).\r
2606 */\r
2607 VOID RebuildTextureSquareInfo()\r
2608 {\r
2609     BITMAP bi;\r
2610     int lite_w = 0;\r
2611     int lite_h = 0;\r
2612     int dark_w = 0;\r
2613     int dark_h = 0;\r
2614     int row;\r
2615     int col;\r
2616 \r
2617     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2618 \r
2619     if( liteBackTexture != NULL ) {\r
2620         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2621             lite_w = bi.bmWidth;\r
2622             lite_h = bi.bmHeight;\r
2623         }\r
2624     }\r
2625 \r
2626     if( darkBackTexture != NULL ) {\r
2627         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2628             dark_w = bi.bmWidth;\r
2629             dark_h = bi.bmHeight;\r
2630         }\r
2631     }\r
2632 \r
2633     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2634         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2635             if( (col + row) & 1 ) {\r
2636                 /* Lite square */\r
2637                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2638                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2639                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2640                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2641                 }\r
2642             }\r
2643             else {\r
2644                 /* Dark square */\r
2645                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2646                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2647                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2648                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2649                 }\r
2650             }\r
2651         }\r
2652     }\r
2653 }\r
2654 \r
2655 /* [AS] Arrow highlighting support */\r
2656 \r
2657 static int A_WIDTH = 5; /* Width of arrow body */\r
2658 \r
2659 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2660 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2661 \r
2662 static double Sqr( double x )\r
2663 {\r
2664     return x*x;\r
2665 }\r
2666 \r
2667 static int Round( double x )\r
2668 {\r
2669     return (int) (x + 0.5);\r
2670 }\r
2671 \r
2672 /* Draw an arrow between two points using current settings */\r
2673 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2674 {\r
2675     POINT arrow[7];\r
2676     double dx, dy, j, k, x, y;\r
2677 \r
2678     if( d_x == s_x ) {\r
2679         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2680 \r
2681         arrow[0].x = s_x + A_WIDTH;\r
2682         arrow[0].y = s_y;\r
2683 \r
2684         arrow[1].x = s_x + A_WIDTH;\r
2685         arrow[1].y = d_y - h;\r
2686 \r
2687         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2688         arrow[2].y = d_y - h;\r
2689 \r
2690         arrow[3].x = d_x;\r
2691         arrow[3].y = d_y;\r
2692 \r
2693         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2694         arrow[4].y = d_y - h;\r
2695 \r
2696         arrow[5].x = s_x - A_WIDTH;\r
2697         arrow[5].y = d_y - h;\r
2698 \r
2699         arrow[6].x = s_x - A_WIDTH;\r
2700         arrow[6].y = s_y;\r
2701     }\r
2702     else if( d_y == s_y ) {\r
2703         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2704 \r
2705         arrow[0].x = s_x;\r
2706         arrow[0].y = s_y + A_WIDTH;\r
2707 \r
2708         arrow[1].x = d_x - w;\r
2709         arrow[1].y = s_y + A_WIDTH;\r
2710 \r
2711         arrow[2].x = d_x - w;\r
2712         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2713 \r
2714         arrow[3].x = d_x;\r
2715         arrow[3].y = d_y;\r
2716 \r
2717         arrow[4].x = d_x - w;\r
2718         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2719 \r
2720         arrow[5].x = d_x - w;\r
2721         arrow[5].y = s_y - A_WIDTH;\r
2722 \r
2723         arrow[6].x = s_x;\r
2724         arrow[6].y = s_y - A_WIDTH;\r
2725     }\r
2726     else {\r
2727         /* [AS] Needed a lot of paper for this! :-) */\r
2728         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2729         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2730   \r
2731         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2732 \r
2733         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2734 \r
2735         x = s_x;\r
2736         y = s_y;\r
2737 \r
2738         arrow[0].x = Round(x - j);\r
2739         arrow[0].y = Round(y + j*dx);\r
2740 \r
2741         arrow[1].x = Round(x + j);\r
2742         arrow[1].y = Round(y - j*dx);\r
2743 \r
2744         if( d_x > s_x ) {\r
2745             x = (double) d_x - k;\r
2746             y = (double) d_y - k*dy;\r
2747         }\r
2748         else {\r
2749             x = (double) d_x + k;\r
2750             y = (double) d_y + k*dy;\r
2751         }\r
2752 \r
2753         arrow[2].x = Round(x + j);\r
2754         arrow[2].y = Round(y - j*dx);\r
2755 \r
2756         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2757         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2758 \r
2759         arrow[4].x = d_x;\r
2760         arrow[4].y = d_y;\r
2761 \r
2762         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2763         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2764 \r
2765         arrow[6].x = Round(x - j);\r
2766         arrow[6].y = Round(y + j*dx);\r
2767     }\r
2768 \r
2769     Polygon( hdc, arrow, 7 );\r
2770 }\r
2771 \r
2772 /* [AS] Draw an arrow between two squares */\r
2773 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2774 {\r
2775     int s_x, s_y, d_x, d_y;\r
2776     HPEN hpen;\r
2777     HPEN holdpen;\r
2778     HBRUSH hbrush;\r
2779     HBRUSH holdbrush;\r
2780     LOGBRUSH stLB;\r
2781 \r
2782     if( s_col == d_col && s_row == d_row ) {\r
2783         return;\r
2784     }\r
2785 \r
2786     /* Get source and destination points */\r
2787     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2788     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2789 \r
2790     if( d_y > s_y ) {\r
2791         d_y += squareSize / 4;\r
2792     }\r
2793     else if( d_y < s_y ) {\r
2794         d_y += 3 * squareSize / 4;\r
2795     }\r
2796     else {\r
2797         d_y += squareSize / 2;\r
2798     }\r
2799 \r
2800     if( d_x > s_x ) {\r
2801         d_x += squareSize / 4;\r
2802     }\r
2803     else if( d_x < s_x ) {\r
2804         d_x += 3 * squareSize / 4;\r
2805     }\r
2806     else {\r
2807         d_x += squareSize / 2;\r
2808     }\r
2809 \r
2810     s_x += squareSize / 2;\r
2811     s_y += squareSize / 2;\r
2812 \r
2813     /* Adjust width */\r
2814     A_WIDTH = squareSize / 14;\r
2815 \r
2816     /* Draw */\r
2817     stLB.lbStyle = BS_SOLID;\r
2818     stLB.lbColor = appData.highlightArrowColor;\r
2819     stLB.lbHatch = 0;\r
2820 \r
2821     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2822     holdpen = SelectObject( hdc, hpen );\r
2823     hbrush = CreateBrushIndirect( &stLB );\r
2824     holdbrush = SelectObject( hdc, hbrush );\r
2825 \r
2826     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2827 \r
2828     SelectObject( hdc, holdpen );\r
2829     SelectObject( hdc, holdbrush );\r
2830     DeleteObject( hpen );\r
2831     DeleteObject( hbrush );\r
2832 }\r
2833 \r
2834 BOOL HasHighlightInfo()\r
2835 {\r
2836     BOOL result = FALSE;\r
2837 \r
2838     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2839         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2840     {\r
2841         result = TRUE;\r
2842     }\r
2843 \r
2844     return result;\r
2845 }\r
2846 \r
2847 BOOL IsDrawArrowEnabled()\r
2848 {\r
2849     BOOL result = FALSE;\r
2850 \r
2851     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2852         result = TRUE;\r
2853     }\r
2854 \r
2855     return result;\r
2856 }\r
2857 \r
2858 VOID DrawArrowHighlight( HDC hdc )\r
2859 {\r
2860     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2861         DrawArrowBetweenSquares( hdc,\r
2862             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2863             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2864     }\r
2865 }\r
2866 \r
2867 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2868 {\r
2869     HRGN result = NULL;\r
2870 \r
2871     if( HasHighlightInfo() ) {\r
2872         int x1, y1, x2, y2;\r
2873         int sx, sy, dx, dy;\r
2874 \r
2875         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2876         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2877 \r
2878         sx = MIN( x1, x2 );\r
2879         sy = MIN( y1, y2 );\r
2880         dx = MAX( x1, x2 ) + squareSize;\r
2881         dy = MAX( y1, y2 ) + squareSize;\r
2882 \r
2883         result = CreateRectRgn( sx, sy, dx, dy );\r
2884     }\r
2885 \r
2886     return result;\r
2887 }\r
2888 \r
2889 /*\r
2890     Warning: this function modifies the behavior of several other functions. \r
2891     \r
2892     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2893     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2894     repaint is scattered all over the place, which is not good for features such as\r
2895     "arrow highlighting" that require a full repaint of the board.\r
2896 \r
2897     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2898     user interaction, when speed is not so important) but especially to avoid errors\r
2899     in the displayed graphics.\r
2900 \r
2901     In such patched places, I always try refer to this function so there is a single\r
2902     place to maintain knowledge.\r
2903     \r
2904     To restore the original behavior, just return FALSE unconditionally.\r
2905 */\r
2906 BOOL IsFullRepaintPreferrable()\r
2907 {\r
2908     BOOL result = FALSE;\r
2909 \r
2910     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2911         /* Arrow may appear on the board */\r
2912         result = TRUE;\r
2913     }\r
2914 \r
2915     return result;\r
2916 }\r
2917 \r
2918 /* \r
2919     This function is called by DrawPosition to know whether a full repaint must\r
2920     be forced or not.\r
2921 \r
2922     Only DrawPosition may directly call this function, which makes use of \r
2923     some state information. Other function should call DrawPosition specifying \r
2924     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2925 */\r
2926 BOOL DrawPositionNeedsFullRepaint()\r
2927 {\r
2928     BOOL result = FALSE;\r
2929 \r
2930     /* \r
2931         Probably a slightly better policy would be to trigger a full repaint\r
2932         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2933         but animation is fast enough that it's difficult to notice.\r
2934     */\r
2935     if( animInfo.piece == EmptySquare ) {\r
2936         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2937             result = TRUE;\r
2938         }\r
2939     }\r
2940 \r
2941     return result;\r
2942 }\r
2943 \r
2944 VOID\r
2945 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2946 {\r
2947   int row, column, x, y, square_color, piece_color;\r
2948   ChessSquare piece;\r
2949   HBRUSH oldBrush;\r
2950   HDC texture_hdc = NULL;\r
2951 \r
2952   /* [AS] Initialize background textures if needed */\r
2953   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2954       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2955       if( backTextureSquareSize != squareSize \r
2956        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2957           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2958           backTextureSquareSize = squareSize;\r
2959           RebuildTextureSquareInfo();\r
2960       }\r
2961 \r
2962       texture_hdc = CreateCompatibleDC( hdc );\r
2963   }\r
2964 \r
2965   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2966     for (column = 0; column < BOARD_WIDTH; column++) {\r
2967   \r
2968       SquareToPos(row, column, &x, &y);\r
2969 \r
2970       piece = board[row][column];\r
2971 \r
2972       square_color = ((column + row) % 2) == 1;\r
2973       if( gameInfo.variant == VariantXiangqi ) {\r
2974           square_color = !InPalace(row, column);\r
2975           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
2976           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
2977       }\r
2978       piece_color = (int) piece < (int) BlackPawn;\r
2979 \r
2980 \r
2981       /* [HGM] holdings file: light square or black */\r
2982       if(column == BOARD_LEFT-2) {\r
2983             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
2984                 square_color = 1;\r
2985             else {\r
2986                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
2987                 continue;\r
2988             }\r
2989       } else\r
2990       if(column == BOARD_RGHT + 1 ) {\r
2991             if( row < gameInfo.holdingsSize )\r
2992                 square_color = 1;\r
2993             else {\r
2994                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
2995                 continue;\r
2996             }\r
2997       }\r
2998       if(column == BOARD_LEFT-1 ) /* left align */\r
2999             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3000       else if( column == BOARD_RGHT) /* right align */\r
3001             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3002       else\r
3003       if (appData.monoMode) {\r
3004         if (piece == EmptySquare) {\r
3005           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3006                  square_color ? WHITENESS : BLACKNESS);\r
3007         } else {\r
3008           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3009         }\r
3010       } \r
3011       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3012           /* [AS] Draw the square using a texture bitmap */\r
3013           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3014           int r = row, c = column; // [HGM] do not flip board in flipView\r
3015           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3016 \r
3017           DrawTile( x, y, \r
3018               squareSize, squareSize, \r
3019               hdc, \r
3020               texture_hdc,\r
3021               backTextureSquareInfo[r][c].mode,\r
3022               backTextureSquareInfo[r][c].x,\r
3023               backTextureSquareInfo[r][c].y );\r
3024 \r
3025           SelectObject( texture_hdc, hbm );\r
3026 \r
3027           if (piece != EmptySquare) {\r
3028               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3029           }\r
3030       }\r
3031       else {\r
3032         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3033 \r
3034         oldBrush = SelectObject(hdc, brush );\r
3035         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3036         SelectObject(hdc, oldBrush);\r
3037         if (piece != EmptySquare)\r
3038           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3039       }\r
3040     }\r
3041   }\r
3042 \r
3043   if( texture_hdc != NULL ) {\r
3044     DeleteDC( texture_hdc );\r
3045   }\r
3046 }\r
3047 \r
3048 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3049 void fputDW(FILE *f, int x)\r
3050 {\r
3051         fputc(x     & 255, f);\r
3052         fputc(x>>8  & 255, f);\r
3053         fputc(x>>16 & 255, f);\r
3054         fputc(x>>24 & 255, f);\r
3055 }\r
3056 \r
3057 #define MAX_CLIPS 200   /* more than enough */\r
3058 \r
3059 VOID\r
3060 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3061 {\r
3062 //  HBITMAP bufferBitmap;\r
3063   BITMAP bi;\r
3064 //  RECT Rect;\r
3065   HDC tmphdc;\r
3066   HBITMAP hbm;\r
3067   int w = 100, h = 50;\r
3068 \r
3069   if(logo == NULL) return;\r
3070 //  GetClientRect(hwndMain, &Rect);\r
3071 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3072 //                                      Rect.bottom-Rect.top+1);\r
3073   tmphdc = CreateCompatibleDC(hdc);\r
3074   hbm = SelectObject(tmphdc, logo);\r
3075   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3076             w = bi.bmWidth;\r
3077             h = bi.bmHeight;\r
3078   }\r
3079   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3080                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3081   SelectObject(tmphdc, hbm);\r
3082   DeleteDC(tmphdc);\r
3083 }\r
3084 \r
3085 VOID\r
3086 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3087 {\r
3088   static Board lastReq, lastDrawn;\r
3089   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3090   static int lastDrawnFlipView = 0;\r
3091   static int lastReqValid = 0, lastDrawnValid = 0;\r
3092   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3093   HDC tmphdc;\r
3094   HDC hdcmem;\r
3095   HBITMAP bufferBitmap;\r
3096   HBITMAP oldBitmap;\r
3097   RECT Rect;\r
3098   HRGN clips[MAX_CLIPS];\r
3099   ChessSquare dragged_piece = EmptySquare;\r
3100 \r
3101   /* I'm undecided on this - this function figures out whether a full\r
3102    * repaint is necessary on its own, so there's no real reason to have the\r
3103    * caller tell it that.  I think this can safely be set to FALSE - but\r
3104    * if we trust the callers not to request full repaints unnessesarily, then\r
3105    * we could skip some clipping work.  In other words, only request a full\r
3106    * redraw when the majority of pieces have changed positions (ie. flip, \r
3107    * gamestart and similar)  --Hawk\r
3108    */\r
3109   Boolean fullrepaint = repaint;\r
3110 \r
3111   if( DrawPositionNeedsFullRepaint() ) {\r
3112       fullrepaint = TRUE;\r
3113   }\r
3114 \r
3115   if (board == NULL) {\r
3116     if (!lastReqValid) {\r
3117       return;\r
3118     }\r
3119     board = lastReq;\r
3120   } else {\r
3121     CopyBoard(lastReq, board);\r
3122     lastReqValid = 1;\r
3123   }\r
3124 \r
3125   if (doingSizing) {\r
3126     return;\r
3127   }\r
3128 \r
3129   if (IsIconic(hwndMain)) {\r
3130     return;\r
3131   }\r
3132 \r
3133   if (hdc == NULL) {\r
3134     hdc = GetDC(hwndMain);\r
3135     if (!appData.monoMode) {\r
3136       SelectPalette(hdc, hPal, FALSE);\r
3137       RealizePalette(hdc);\r
3138     }\r
3139     releaseDC = TRUE;\r
3140   } else {\r
3141     releaseDC = FALSE;\r
3142   }\r
3143 \r
3144   /* Create some work-DCs */\r
3145   hdcmem = CreateCompatibleDC(hdc);\r
3146   tmphdc = CreateCompatibleDC(hdc);\r
3147 \r
3148   /* If dragging is in progress, we temporarely remove the piece */\r
3149   /* [HGM] or temporarily decrease count if stacked              */\r
3150   /*       !! Moved to before board compare !!                   */\r
3151   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3152     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3153     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3154             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3155         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3156     } else \r
3157     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3158             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3159         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3160     } else \r
3161         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3162   }\r
3163 \r
3164   /* Figure out which squares need updating by comparing the \r
3165    * newest board with the last drawn board and checking if\r
3166    * flipping has changed.\r
3167    */\r
3168   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3169     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3170       for (column = 0; column < BOARD_WIDTH; column++) {\r
3171         if (lastDrawn[row][column] != board[row][column]) {\r
3172           SquareToPos(row, column, &x, &y);\r
3173           clips[num_clips++] =\r
3174             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3175         }\r
3176       }\r
3177     }\r
3178     for (i=0; i<2; i++) {\r
3179       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3180           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3181         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3182             lastDrawnHighlight.sq[i].y >= 0) {\r
3183           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3184                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3185           clips[num_clips++] =\r
3186             CreateRectRgn(x - lineGap, y - lineGap, \r
3187                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3188         }\r
3189         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3190           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3191           clips[num_clips++] =\r
3192             CreateRectRgn(x - lineGap, y - lineGap, \r
3193                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3194         }\r
3195       }\r
3196     }\r
3197     for (i=0; i<2; i++) {\r
3198       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3199           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3200         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3201             lastDrawnPremove.sq[i].y >= 0) {\r
3202           SquareToPos(lastDrawnPremove.sq[i].y,\r
3203                       lastDrawnPremove.sq[i].x, &x, &y);\r
3204           clips[num_clips++] =\r
3205             CreateRectRgn(x - lineGap, y - lineGap, \r
3206                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3207         }\r
3208         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3209             premoveHighlightInfo.sq[i].y >= 0) {\r
3210           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3211                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3212           clips[num_clips++] =\r
3213             CreateRectRgn(x - lineGap, y - lineGap, \r
3214                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3215         }\r
3216       }\r
3217     }\r
3218   } else {\r
3219     fullrepaint = TRUE;\r
3220   }\r
3221 \r
3222   /* Create a buffer bitmap - this is the actual bitmap\r
3223    * being written to.  When all the work is done, we can\r
3224    * copy it to the real DC (the screen).  This avoids\r
3225    * the problems with flickering.\r
3226    */\r
3227   GetClientRect(hwndMain, &Rect);\r
3228   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3229                                         Rect.bottom-Rect.top+1);\r
3230   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3231   if (!appData.monoMode) {\r
3232     SelectPalette(hdcmem, hPal, FALSE);\r
3233   }\r
3234 \r
3235   /* Create clips for dragging */\r
3236   if (!fullrepaint) {\r
3237     if (dragInfo.from.x >= 0) {\r
3238       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3239       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3240     }\r
3241     if (dragInfo.start.x >= 0) {\r
3242       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3243       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3244     }\r
3245     if (dragInfo.pos.x >= 0) {\r
3246       x = dragInfo.pos.x - squareSize / 2;\r
3247       y = dragInfo.pos.y - squareSize / 2;\r
3248       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3249     }\r
3250     if (dragInfo.lastpos.x >= 0) {\r
3251       x = dragInfo.lastpos.x - squareSize / 2;\r
3252       y = dragInfo.lastpos.y - squareSize / 2;\r
3253       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3254     }\r
3255   }\r
3256 \r
3257   /* Are we animating a move?  \r
3258    * If so, \r
3259    *   - remove the piece from the board (temporarely)\r
3260    *   - calculate the clipping region\r
3261    */\r
3262   if (!fullrepaint) {\r
3263     if (animInfo.piece != EmptySquare) {\r
3264       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3265       x = boardRect.left + animInfo.lastpos.x;\r
3266       y = boardRect.top + animInfo.lastpos.y;\r
3267       x2 = boardRect.left + animInfo.pos.x;\r
3268       y2 = boardRect.top + animInfo.pos.y;\r
3269       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3270       /* Slight kludge.  The real problem is that after AnimateMove is\r
3271          done, the position on the screen does not match lastDrawn.\r
3272          This currently causes trouble only on e.p. captures in\r
3273          atomic, where the piece moves to an empty square and then\r
3274          explodes.  The old and new positions both had an empty square\r
3275          at the destination, but animation has drawn a piece there and\r
3276          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3277       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3278     }\r
3279   }\r
3280 \r
3281   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3282   if (num_clips == 0)\r
3283     fullrepaint = TRUE;\r
3284 \r
3285   /* Set clipping on the memory DC */\r
3286   if (!fullrepaint) {\r
3287     SelectClipRgn(hdcmem, clips[0]);\r
3288     for (x = 1; x < num_clips; x++) {\r
3289       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3290         abort();  // this should never ever happen!\r
3291     }\r
3292   }\r
3293 \r
3294   /* Do all the drawing to the memory DC */\r
3295   if(explodeInfo.radius) { // [HGM] atomic\r
3296         HBRUSH oldBrush;\r
3297         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3298         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3299         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3300         x += squareSize/2;\r
3301         y += squareSize/2;\r
3302         if(!fullrepaint) {\r
3303           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3304           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3305         }\r
3306         DrawGridOnDC(hdcmem);\r
3307         DrawHighlightsOnDC(hdcmem);\r
3308         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3309         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3310         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3311         SelectObject(hdcmem, oldBrush);\r
3312   } else {\r
3313     DrawGridOnDC(hdcmem);\r
3314     DrawHighlightsOnDC(hdcmem);\r
3315     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3316   }\r
3317   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3318     for (column = 0; column < BOARD_WIDTH; column++) {\r
3319         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3320             HBRUSH oldBrush = SelectObject(hdcmem, \r
3321                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3322             SquareToPos(row, column, &x, &y);\r
3323             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3324                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3325             SelectObject(hdcmem, oldBrush);\r
3326         }\r
3327     }\r
3328   }\r
3329   if(logoHeight) {\r
3330         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3331         if(appData.autoLogo) {\r
3332           \r
3333           switch(gameMode) { // pick logos based on game mode\r
3334             case IcsObserving:\r
3335                 whiteLogo = second.programLogo; // ICS logo\r
3336                 blackLogo = second.programLogo;\r
3337             default:\r
3338                 break;\r
3339             case IcsPlayingWhite:\r
3340                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3341                 blackLogo = second.programLogo; // ICS logo\r
3342                 break;\r
3343             case IcsPlayingBlack:\r
3344                 whiteLogo = second.programLogo; // ICS logo\r
3345                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3346                 break;\r
3347             case TwoMachinesPlay:\r
3348                 if(first.twoMachinesColor[0] == 'b') {\r
3349                     whiteLogo = second.programLogo;\r
3350                     blackLogo = first.programLogo;\r
3351                 }\r
3352                 break;\r
3353             case MachinePlaysWhite:\r
3354                 blackLogo = userLogo;\r
3355                 break;\r
3356             case MachinePlaysBlack:\r
3357                 whiteLogo = userLogo;\r
3358                 blackLogo = first.programLogo;\r
3359           }\r
3360         }\r
3361         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3362         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3363   }\r
3364 \r
3365   if( appData.highlightMoveWithArrow ) {\r
3366     DrawArrowHighlight(hdcmem);\r
3367   }\r
3368 \r
3369   DrawCoordsOnDC(hdcmem);\r
3370 \r
3371   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
3372                  /* to make sure lastDrawn contains what is actually drawn */\r
3373 \r
3374   /* Put the dragged piece back into place and draw it (out of place!) */\r
3375     if (dragged_piece != EmptySquare) {\r
3376     /* [HGM] or restack */\r
3377     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3378                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3379     else\r
3380     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3381                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3382     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3383     x = dragInfo.pos.x - squareSize / 2;\r
3384     y = dragInfo.pos.y - squareSize / 2;\r
3385     DrawPieceOnDC(hdcmem, dragged_piece,\r
3386                   ((int) dragged_piece < (int) BlackPawn), \r
3387                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3388   }   \r
3389   \r
3390   /* Put the animated piece back into place and draw it */\r
3391   if (animInfo.piece != EmptySquare) {\r
3392     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3393     x = boardRect.left + animInfo.pos.x;\r
3394     y = boardRect.top + animInfo.pos.y;\r
3395     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3396                   ((int) animInfo.piece < (int) BlackPawn),\r
3397                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3398   }\r
3399 \r
3400   /* Release the bufferBitmap by selecting in the old bitmap \r
3401    * and delete the memory DC\r
3402    */\r
3403   SelectObject(hdcmem, oldBitmap);\r
3404   DeleteDC(hdcmem);\r
3405 \r
3406   /* Set clipping on the target DC */\r
3407   if (!fullrepaint) {\r
3408     SelectClipRgn(hdc, clips[0]);\r
3409     for (x = 1; x < num_clips; x++) {\r
3410       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3411         abort();   // this should never ever happen!\r
3412     } \r
3413   }\r
3414 \r
3415   /* Copy the new bitmap onto the screen in one go.\r
3416    * This way we avoid any flickering\r
3417    */\r
3418   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3419   BitBlt(hdc, boardRect.left, boardRect.top,\r
3420          boardRect.right - boardRect.left,\r
3421          boardRect.bottom - boardRect.top,\r
3422          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3423   if(saveDiagFlag) { \r
3424     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3425     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3426 \r
3427     GetObject(bufferBitmap, sizeof(b), &b);\r
3428     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3429         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3430         bih.biWidth = b.bmWidth;\r
3431         bih.biHeight = b.bmHeight;\r
3432         bih.biPlanes = 1;\r
3433         bih.biBitCount = b.bmBitsPixel;\r
3434         bih.biCompression = 0;\r
3435         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3436         bih.biXPelsPerMeter = 0;\r
3437         bih.biYPelsPerMeter = 0;\r
3438         bih.biClrUsed = 0;\r
3439         bih.biClrImportant = 0;\r
3440 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3441 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3442         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3443 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3444 \r
3445         wb = b.bmWidthBytes;\r
3446         // count colors\r
3447         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3448                 int k = ((int*) pData)[i];\r
3449                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3450                 if(j >= 16) break;\r
3451                 color[j] = k;\r
3452                 if(j >= nrColors) nrColors = j+1;\r
3453         }\r
3454         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3455                 INT p = 0;\r
3456                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3457                     for(w=0; w<(wb>>2); w+=2) {\r
3458                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3459                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3460                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3461                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3462                         pData[p++] = m | j<<4;\r
3463                     }\r
3464                     while(p&3) pData[p++] = 0;\r
3465                 }\r
3466                 fac = 3;\r
3467                 wb = ((wb+31)>>5)<<2;\r
3468         }\r
3469         // write BITMAPFILEHEADER\r
3470         fprintf(diagFile, "BM");\r
3471         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3472         fputDW(diagFile, 0);\r
3473         fputDW(diagFile, 0x36 + (fac?64:0));\r
3474         // write BITMAPINFOHEADER\r
3475         fputDW(diagFile, 40);\r
3476         fputDW(diagFile, b.bmWidth);\r
3477         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3478         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3479         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3480         fputDW(diagFile, 0);\r
3481         fputDW(diagFile, 0);\r
3482         fputDW(diagFile, 0);\r
3483         fputDW(diagFile, 0);\r
3484         fputDW(diagFile, 0);\r
3485         fputDW(diagFile, 0);\r
3486         // write color table\r
3487         if(fac)\r
3488         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3489         // write bitmap data\r
3490         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3491                 fputc(pData[i], diagFile);\r
3492      }\r
3493   }\r
3494 \r
3495   SelectObject(tmphdc, oldBitmap);\r
3496 \r
3497   /* Massive cleanup */\r
3498   for (x = 0; x < num_clips; x++)\r
3499     DeleteObject(clips[x]);\r
3500 \r
3501   DeleteDC(tmphdc);\r
3502   DeleteObject(bufferBitmap);\r
3503 \r
3504   if (releaseDC) \r
3505     ReleaseDC(hwndMain, hdc);\r
3506   \r
3507   if (lastDrawnFlipView != flipView) {\r
3508     if (flipView)\r
3509       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3510     else\r
3511       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3512   }\r
3513 \r
3514 /*  CopyBoard(lastDrawn, board);*/\r
3515   lastDrawnHighlight = highlightInfo;\r
3516   lastDrawnPremove   = premoveHighlightInfo;\r
3517   lastDrawnFlipView = flipView;\r
3518   lastDrawnValid = 1;\r
3519 }\r
3520 \r
3521 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3522 int\r
3523 SaveDiagram(f)\r
3524      FILE *f;\r
3525 {\r
3526     saveDiagFlag = 1; diagFile = f;\r
3527     HDCDrawPosition(NULL, TRUE, NULL);\r
3528 \r
3529     saveDiagFlag = 0;\r
3530 \r
3531 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3532     \r
3533     fclose(f);\r
3534     return TRUE;\r
3535 }\r
3536 \r
3537 \r
3538 /*---------------------------------------------------------------------------*\\r
3539 | CLIENT PAINT PROCEDURE\r
3540 |   This is the main event-handler for the WM_PAINT message.\r
3541 |\r
3542 \*---------------------------------------------------------------------------*/\r
3543 VOID\r
3544 PaintProc(HWND hwnd)\r
3545 {\r
3546   HDC         hdc;\r
3547   PAINTSTRUCT ps;\r
3548   HFONT       oldFont;\r
3549 \r
3550   if((hdc = BeginPaint(hwnd, &ps))) {\r
3551     if (IsIconic(hwnd)) {\r
3552       DrawIcon(hdc, 2, 2, iconCurrent);\r
3553     } else {\r
3554       if (!appData.monoMode) {\r
3555         SelectPalette(hdc, hPal, FALSE);\r
3556         RealizePalette(hdc);\r
3557       }\r
3558       HDCDrawPosition(hdc, 1, NULL);\r
3559       oldFont =\r
3560         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3561       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3562                  ETO_CLIPPED|ETO_OPAQUE,\r
3563                  &messageRect, messageText, strlen(messageText), NULL);\r
3564       SelectObject(hdc, oldFont);\r
3565       DisplayBothClocks();\r
3566     }\r
3567     EndPaint(hwnd,&ps);\r
3568   }\r
3569 \r
3570   return;\r
3571 }\r
3572 \r
3573 \r
3574 /*\r
3575  * If the user selects on a border boundary, return -1; if off the board,\r
3576  *   return -2.  Otherwise map the event coordinate to the square.\r
3577  * The offset boardRect.left or boardRect.top must already have been\r
3578  *   subtracted from x.\r
3579  */\r
3580 int EventToSquare(x, limit)\r
3581      int x, limit;\r
3582 {\r
3583   if (x <= 0)\r
3584     return -2;\r
3585   if (x < lineGap)\r
3586     return -1;\r
3587   x -= lineGap;\r
3588   if ((x % (squareSize + lineGap)) >= squareSize)\r
3589     return -1;\r
3590   x /= (squareSize + lineGap);\r
3591     if (x >= limit)\r
3592     return -2;\r
3593   return x;\r
3594 }\r
3595 \r
3596 typedef struct {\r
3597   char piece;\r
3598   int command;\r
3599   char* name;\r
3600 } DropEnable;\r
3601 \r
3602 DropEnable dropEnables[] = {\r
3603   { 'P', DP_Pawn, "Pawn" },\r
3604   { 'N', DP_Knight, "Knight" },\r
3605   { 'B', DP_Bishop, "Bishop" },\r
3606   { 'R', DP_Rook, "Rook" },\r
3607   { 'Q', DP_Queen, "Queen" },\r
3608 };\r
3609 \r
3610 VOID\r
3611 SetupDropMenu(HMENU hmenu)\r
3612 {\r
3613   int i, count, enable;\r
3614   char *p;\r
3615   extern char white_holding[], black_holding[];\r
3616   char item[MSG_SIZ];\r
3617 \r
3618   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3619     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3620                dropEnables[i].piece);\r
3621     count = 0;\r
3622     while (p && *p++ == dropEnables[i].piece) count++;\r
3623     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
3624     enable = count > 0 || !appData.testLegality\r
3625       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3626                       && !appData.icsActive);\r
3627     ModifyMenu(hmenu, dropEnables[i].command,\r
3628                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3629                dropEnables[i].command, item);\r
3630   }\r
3631 }\r
3632 \r
3633 void DragPieceBegin(int x, int y)\r
3634 {\r
3635       dragInfo.lastpos.x = boardRect.left + x;\r
3636       dragInfo.lastpos.y = boardRect.top + y;\r
3637       dragInfo.from.x = fromX;\r
3638       dragInfo.from.y = fromY;\r
3639       dragInfo.start = dragInfo.from;\r
3640       SetCapture(hwndMain);\r
3641 }\r
3642 \r
3643 void DragPieceEnd(int x, int y)\r
3644 {\r
3645     ReleaseCapture();\r
3646     dragInfo.start.x = dragInfo.start.y = -1;\r
3647     dragInfo.from = dragInfo.start;\r
3648     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3649 }\r
3650 \r
3651 /* Event handler for mouse messages */\r
3652 VOID\r
3653 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3654 {\r
3655   int x, y;\r
3656   POINT pt;\r
3657   static int recursive = 0;\r
3658   HMENU hmenu;\r
3659   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
3660 \r
3661   if (recursive) {\r
3662     if (message == WM_MBUTTONUP) {\r
3663       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
3664          to the middle button: we simulate pressing the left button too!\r
3665          */\r
3666       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
3667       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
3668     }\r
3669     return;\r
3670   }\r
3671   recursive++;\r
3672   \r
3673   pt.x = LOWORD(lParam);\r
3674   pt.y = HIWORD(lParam);\r
3675   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
3676   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
3677   if (!flipView && y >= 0) {\r
3678     y = BOARD_HEIGHT - 1 - y;\r
3679   }\r
3680   if (flipView && x >= 0) {\r
3681     x = BOARD_WIDTH - 1 - x;\r
3682   }\r
3683 \r
3684   switch (message) {\r
3685   case WM_LBUTTONDOWN:\r
3686       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3687         if (gameMode == EditPosition) {\r
3688           SetWhiteToPlayEvent();\r
3689         } else if (gameMode == IcsPlayingBlack ||\r
3690                    gameMode == MachinePlaysWhite) {\r
3691           CallFlagEvent();\r
3692         } else if (gameMode == EditGame) {\r
3693           AdjustClock(flipClock, -1);\r
3694         }\r
3695       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3696         if (gameMode == EditPosition) {\r
3697           SetBlackToPlayEvent();\r
3698         } else if (gameMode == IcsPlayingWhite ||\r
3699                    gameMode == MachinePlaysBlack) {\r
3700           CallFlagEvent();\r
3701         } else if (gameMode == EditGame) {\r
3702           AdjustClock(!flipClock, -1);\r
3703         }\r
3704       }\r
3705       dragInfo.start.x = dragInfo.start.y = -1;\r
3706       dragInfo.from = dragInfo.start;\r
3707     if(fromX == -1 && frozen) { // not sure where this is for\r
3708                 fromX = fromY = -1; \r
3709       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
3710       break;\r
3711     }\r
3712       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
3713       DrawPosition(TRUE, NULL);\r
3714     break;\r
3715 \r
3716   case WM_LBUTTONUP:\r
3717       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
3718       DrawPosition(TRUE, NULL);\r
3719     break;\r
3720 \r
3721   case WM_MOUSEMOVE:\r
3722     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
3723     if ((appData.animateDragging || appData.highlightDragging)\r
3724         && (wParam & MK_LBUTTON)\r
3725         && dragInfo.from.x >= 0) \r
3726     {\r
3727       BOOL full_repaint = FALSE;\r
3728 \r
3729       if (appData.animateDragging) {\r
3730         dragInfo.pos = pt;\r
3731       }\r
3732       if (appData.highlightDragging) {\r
3733         SetHighlights(fromX, fromY, x, y);\r
3734         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
3735             full_repaint = TRUE;\r
3736         }\r
3737       }\r
3738       \r
3739       DrawPosition( full_repaint, NULL);\r
3740       \r
3741       dragInfo.lastpos = dragInfo.pos;\r
3742     }\r
3743     break;\r
3744 \r
3745   case WM_MOUSEWHEEL: // [DM]\r
3746     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
3747        /* Mouse Wheel is being rolled forward\r
3748         * Play moves forward\r
3749         */\r
3750        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
3751                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
3752        /* Mouse Wheel is being rolled backward\r
3753         * Play moves backward\r
3754         */\r
3755        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
3756                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
3757     }\r
3758     break;\r
3759 \r
3760   case WM_MBUTTONUP:
3761   case WM_RBUTTONUP:
3762     ReleaseCapture();
3763     UnLoadPV();
3764     break;
3765  
3766   case WM_MBUTTONDOWN:\r
3767   case WM_RBUTTONDOWN:\r
3768     ErrorPopDown();\r
3769     ReleaseCapture();\r
3770     fromX = fromY = -1;\r
3771     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3772     dragInfo.start.x = dragInfo.start.y = -1;\r
3773     dragInfo.from = dragInfo.start;\r
3774     dragInfo.lastpos = dragInfo.pos;\r
3775     if (appData.highlightDragging) {\r
3776       ClearHighlights();\r
3777     }\r
3778     if(y == -2) {\r
3779       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
3780       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3781           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
3782       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3783           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
3784       }\r
3785     }\r
3786     DrawPosition(TRUE, NULL);\r
3787 \r
3788     switch (gameMode) {\r
3789     case EditPosition:\r
3790     case IcsExamining:\r
3791       if (x < 0 || y < 0) break;\r
3792       fromX = x;\r
3793       fromY = y;\r
3794       if (message == WM_MBUTTONDOWN) {\r
3795         buttonCount = 3;  /* even if system didn't think so */\r
3796         if (wParam & MK_SHIFT) \r
3797           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3798         else\r
3799           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3800       } else { /* message == WM_RBUTTONDOWN */\r
3801         /* Just have one menu, on the right button.  Windows users don't\r
3802            think to try the middle one, and sometimes other software steals\r
3803            it, or it doesn't really exist. */\r
3804         if(gameInfo.variant != VariantShogi)\r
3805             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3806         else\r
3807             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
3808       }\r
3809       break;\r
3810     case IcsObserving:
3811       if(!appData.icsEngineAnalyze) break;
3812     case IcsPlayingWhite:
3813     case IcsPlayingBlack:
3814       if(!appData.zippyPlay) goto noZip;
3815     case MachinePlaysWhite:
3816     case MachinePlaysBlack:
3817     case TwoMachinesPlay:
3818     case AnalyzeMode:
3819     case AnalyzeFile:
3820       if (!appData.dropMenu) {
3821         SetCapture(hwndMain);
3822         LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);
3823         break;
3824       }
3825       if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
3826          gameMode == AnalyzeFile || gameMode == IcsObserving) break;
3827     case EditGame:
3828     noZip:
3829       if (x < 0 || y < 0) break;
3830       if (!appData.dropMenu || appData.testLegality &&
3831           gameInfo.variant != VariantBughouse &&
3832           gameInfo.variant != VariantCrazyhouse) break;
3833       fromX = x;\r
3834       fromY = y;\r
3835       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3836       SetupDropMenu(hmenu);\r
3837       MenuPopup(hwnd, pt, hmenu, -1);\r
3838       break;\r
3839     default:\r
3840       break;\r
3841     }\r
3842     break;\r
3843   }\r
3844 \r
3845   recursive--;\r
3846 }\r
3847 \r
3848 /* Preprocess messages for buttons in main window */\r
3849 LRESULT CALLBACK\r
3850 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3851 {\r
3852   int id = GetWindowLong(hwnd, GWL_ID);\r
3853   int i, dir;\r
3854 \r
3855   for (i=0; i<N_BUTTONS; i++) {\r
3856     if (buttonDesc[i].id == id) break;\r
3857   }\r
3858   if (i == N_BUTTONS) return 0;\r
3859   switch (message) {\r
3860   case WM_KEYDOWN:\r
3861     switch (wParam) {\r
3862     case VK_LEFT:\r
3863     case VK_RIGHT:\r
3864       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3865       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3866       return TRUE;\r
3867     }\r
3868     break;\r
3869   case WM_CHAR:\r
3870     switch (wParam) {\r
3871     case '\r':\r
3872       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3873       return TRUE;\r
3874     default:\r
3875       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
3876         // [HGM] movenum: only letters or leading zero should go to ICS input\r
3877         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3878         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3879         SetFocus(h);\r
3880         SendMessage(h, WM_CHAR, wParam, lParam);\r
3881         return TRUE;\r
3882       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3883         PopUpMoveDialog((char)wParam);\r
3884       }\r
3885       break;\r
3886     }\r
3887     break;\r
3888   }\r
3889   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3890 }\r
3891 \r
3892 /* Process messages for Promotion dialog box */\r
3893 LRESULT CALLBACK\r
3894 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3895 {\r
3896   char promoChar;\r
3897 \r
3898   switch (message) {\r
3899   case WM_INITDIALOG: /* message: initialize dialog box */\r
3900     /* Center the dialog over the application window */\r
3901     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
3902     ShowWindow(GetDlgItem(hDlg, PB_King), \r
3903       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
3904        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
3905                SW_SHOW : SW_HIDE);\r
3906     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
3907     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
3908        ((PieceToChar(WhiteAngel) >= 'A' &&\r
3909          PieceToChar(WhiteAngel) != '~') ||\r
3910         (PieceToChar(BlackAngel) >= 'A' &&\r
3911          PieceToChar(BlackAngel) != '~')   ) ?\r
3912                SW_SHOW : SW_HIDE);\r
3913     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
3914        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
3915          PieceToChar(WhiteMarshall) != '~') ||\r
3916         (PieceToChar(BlackMarshall) >= 'A' &&\r
3917          PieceToChar(BlackMarshall) != '~')   ) ?\r
3918                SW_SHOW : SW_HIDE);\r
3919     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
3920     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
3921        gameInfo.variant != VariantShogi ?\r
3922                SW_SHOW : SW_HIDE);\r
3923     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
3924        gameInfo.variant != VariantShogi ?\r
3925                SW_SHOW : SW_HIDE);\r
3926     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
3927        gameInfo.variant == VariantShogi ?\r
3928                SW_SHOW : SW_HIDE);\r
3929     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
3930        gameInfo.variant == VariantShogi ?\r
3931                SW_SHOW : SW_HIDE);\r
3932     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
3933        gameInfo.variant == VariantSuper ?\r
3934                SW_SHOW : SW_HIDE);\r
3935     return TRUE;\r
3936 \r
3937   case WM_COMMAND: /* message: received a command */\r
3938     switch (LOWORD(wParam)) {\r
3939     case IDCANCEL:\r
3940       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3941       ClearHighlights();\r
3942       DrawPosition(FALSE, NULL);\r
3943       return TRUE;\r
3944     case PB_King:\r
3945       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
3946       break;\r
3947     case PB_Queen:\r
3948       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
3949       break;\r
3950     case PB_Rook:\r
3951       promoChar = PieceToChar(BlackRook);\r
3952       break;\r
3953     case PB_Bishop:\r
3954       promoChar = PieceToChar(BlackBishop);\r
3955       break;\r
3956     case PB_Chancellor:\r
3957       promoChar = PieceToChar(BlackMarshall);\r
3958       break;\r
3959     case PB_Archbishop:\r
3960       promoChar = PieceToChar(BlackAngel);\r
3961       break;\r
3962     case PB_Knight:\r
3963       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
3964       break;\r
3965     default:\r
3966       return FALSE;\r
3967     }\r
3968     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3969     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
3970        only show the popup when we are already sure the move is valid or\r
3971        legal. We pass a faulty move type, but the kludge is that FinishMove\r
3972        will figure out it is a promotion from the promoChar. */\r
3973     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3974     fromX = fromY = -1;\r
3975     if (!appData.highlightLastMove) {\r
3976       ClearHighlights();\r
3977       DrawPosition(FALSE, NULL);\r
3978     }\r
3979     return TRUE;\r
3980   }\r
3981   return FALSE;\r
3982 }\r
3983 \r
3984 /* Pop up promotion dialog */\r
3985 VOID\r
3986 PromotionPopup(HWND hwnd)\r
3987 {\r
3988   FARPROC lpProc;\r
3989 \r
3990   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3991   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3992     hwnd, (DLGPROC)lpProc);\r
3993   FreeProcInstance(lpProc);\r
3994 }\r
3995 \r
3996 void\r
3997 PromotionPopUp()\r
3998 {\r
3999   DrawPosition(TRUE, NULL);\r
4000   PromotionPopup(hwndMain);\r
4001 }\r
4002 \r
4003 /* Toggle ShowThinking */\r
4004 VOID\r
4005 ToggleShowThinking()\r
4006 {\r
4007   appData.showThinking = !appData.showThinking;\r
4008   ShowThinkingEvent();\r
4009 }\r
4010 \r
4011 VOID\r
4012 LoadGameDialog(HWND hwnd, char* title)\r
4013 {\r
4014   UINT number = 0;\r
4015   FILE *f;\r
4016   char fileTitle[MSG_SIZ];\r
4017   f = OpenFileDialog(hwnd, "rb", "",\r
4018                      appData.oldSaveStyle ? "gam" : "pgn",\r
4019                      GAME_FILT,\r
4020                      title, &number, fileTitle, NULL);\r
4021   if (f != NULL) {\r
4022     cmailMsgLoaded = FALSE;\r
4023     if (number == 0) {\r
4024       int error = GameListBuild(f);\r
4025       if (error) {\r
4026         DisplayError("Cannot build game list", error);\r
4027       } else if (!ListEmpty(&gameList) &&\r
4028                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4029         GameListPopUp(f, fileTitle);\r
4030         return;\r
4031       }\r
4032       GameListDestroy();\r
4033       number = 1;\r
4034     }\r
4035     LoadGame(f, number, fileTitle, FALSE);\r
4036   }\r
4037 }\r
4038 \r
4039 int get_term_width()\r
4040 {\r
4041     HDC hdc;\r
4042     TEXTMETRIC tm;\r
4043     RECT rc;\r
4044     HFONT hfont, hold_font;\r
4045     LOGFONT lf;\r
4046     HWND hText;\r
4047 \r
4048     if (hwndConsole)\r
4049         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4050     else\r
4051         return 79;\r
4052 \r
4053     // get the text metrics\r
4054     hdc = GetDC(hText);\r
4055     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4056     if (consoleCF.dwEffects & CFE_BOLD)\r
4057         lf.lfWeight = FW_BOLD;\r
4058     if (consoleCF.dwEffects & CFE_ITALIC)\r
4059         lf.lfItalic = TRUE;\r
4060     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4061         lf.lfStrikeOut = TRUE;\r
4062     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4063         lf.lfUnderline = TRUE;\r
4064     hfont = CreateFontIndirect(&lf);\r
4065     hold_font = SelectObject(hdc, hfont);\r
4066     GetTextMetrics(hdc, &tm);\r
4067     SelectObject(hdc, hold_font);\r
4068     DeleteObject(hfont);\r
4069     ReleaseDC(hText, hdc);\r
4070 \r
4071     // get the rectangle\r
4072     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4073 \r
4074     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4075 }\r
4076 \r
4077 void UpdateICSWidth(HWND hText)\r
4078 {\r
4079     LONG old_width, new_width;\r
4080 \r
4081     new_width = get_term_width(hText, FALSE);\r
4082     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4083     if (new_width != old_width)\r
4084     {\r
4085         ics_update_width(new_width);\r
4086         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4087     }\r
4088 }\r
4089 \r
4090 VOID\r
4091 ChangedConsoleFont()\r
4092 {\r
4093   CHARFORMAT cfmt;\r
4094   CHARRANGE tmpsel, sel;\r
4095   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4096   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4097   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4098   PARAFORMAT paraf;\r
4099 \r
4100   cfmt.cbSize = sizeof(CHARFORMAT);\r
4101   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4102   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4103   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4104    * size.  This was undocumented in the version of MSVC++ that I had\r
4105    * when I wrote the code, but is apparently documented now.\r
4106    */\r
4107   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4108   cfmt.bCharSet = f->lf.lfCharSet;\r
4109   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4110   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4111   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4112   /* Why are the following seemingly needed too? */\r
4113   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4114   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4115   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4116   tmpsel.cpMin = 0;\r
4117   tmpsel.cpMax = -1; /*999999?*/\r
4118   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4119   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4120   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4121    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4122    */\r
4123   paraf.cbSize = sizeof(paraf);\r
4124   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4125   paraf.dxStartIndent = 0;\r
4126   paraf.dxOffset = WRAP_INDENT;\r
4127   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4128   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4129   UpdateICSWidth(hText);\r
4130 }\r
4131 \r
4132 /*---------------------------------------------------------------------------*\\r
4133  *\r
4134  * Window Proc for main window\r
4135  *\r
4136 \*---------------------------------------------------------------------------*/\r
4137 \r
4138 /* Process messages for main window, etc. */\r
4139 LRESULT CALLBACK\r
4140 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4141 {\r
4142   FARPROC lpProc;\r
4143   int wmId, wmEvent;\r
4144   char *defName;\r
4145   FILE *f;\r
4146   UINT number;\r
4147   char fileTitle[MSG_SIZ];\r
4148   char buf[MSG_SIZ];\r
4149   static SnapData sd;\r
4150 \r
4151   switch (message) {\r
4152 \r
4153   case WM_PAINT: /* message: repaint portion of window */\r
4154     PaintProc(hwnd);\r
4155     break;\r
4156 \r
4157   case WM_ERASEBKGND:\r
4158     if (IsIconic(hwnd)) {\r
4159       /* Cheat; change the message */\r
4160       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4161     } else {\r
4162       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4163     }\r
4164     break;\r
4165 \r
4166   case WM_LBUTTONDOWN:\r
4167   case WM_MBUTTONDOWN:\r
4168   case WM_RBUTTONDOWN:\r
4169   case WM_LBUTTONUP:\r
4170   case WM_MBUTTONUP:\r
4171   case WM_RBUTTONUP:\r
4172   case WM_MOUSEMOVE:\r
4173   case WM_MOUSEWHEEL:\r
4174     MouseEvent(hwnd, message, wParam, lParam);\r
4175     break;\r
4176 \r
4177   JAWS_KB_NAVIGATION\r
4178 \r
4179   case WM_CHAR:\r
4180     \r
4181     JAWS_ALT_INTERCEPT\r
4182 \r
4183     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4184         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4185         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4186         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4187         SetFocus(h);\r
4188         SendMessage(h, message, wParam, lParam);\r
4189     } else if(lParam != KF_REPEAT) {\r
4190         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4191                 PopUpMoveDialog((char)wParam);\r
4192         } else if((char)wParam == 003) CopyGameToClipboard();\r
4193          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4194     }\r
4195 \r
4196     break;\r
4197 \r
4198   case WM_PALETTECHANGED:\r
4199     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4200       int nnew;\r
4201       HDC hdc = GetDC(hwndMain);\r
4202       SelectPalette(hdc, hPal, TRUE);\r
4203       nnew = RealizePalette(hdc);\r
4204       if (nnew > 0) {\r
4205         paletteChanged = TRUE;\r
4206         InvalidateRect(hwnd, &boardRect, FALSE);\r
4207       }\r
4208       ReleaseDC(hwnd, hdc);\r
4209     }\r
4210     break;\r
4211 \r
4212   case WM_QUERYNEWPALETTE:\r
4213     if (!appData.monoMode /*&& paletteChanged*/) {\r
4214       int nnew;\r
4215       HDC hdc = GetDC(hwndMain);\r
4216       paletteChanged = FALSE;\r
4217       SelectPalette(hdc, hPal, FALSE);\r
4218       nnew = RealizePalette(hdc);\r
4219       if (nnew > 0) {\r
4220         InvalidateRect(hwnd, &boardRect, FALSE);\r
4221       }\r
4222       ReleaseDC(hwnd, hdc);\r
4223       return TRUE;\r
4224     }\r
4225     return FALSE;\r
4226 \r
4227   case WM_COMMAND: /* message: command from application menu */\r
4228     wmId    = LOWORD(wParam);\r
4229     wmEvent = HIWORD(wParam);\r
4230 \r
4231     switch (wmId) {\r
4232     case IDM_NewGame:\r
4233       ResetGameEvent();\r
4234       SAY("new game enter a move to play against the computer with white");\r
4235       break;\r
4236 \r
4237     case IDM_NewGameFRC:\r
4238       if( NewGameFRC() == 0 ) {\r
4239         ResetGameEvent();\r
4240       }\r
4241       break;\r
4242 \r
4243     case IDM_NewVariant:\r
4244       NewVariantPopup(hwnd);\r
4245       break;\r
4246 \r
4247     case IDM_LoadGame:\r
4248       LoadGameDialog(hwnd, "Load Game from File");\r
4249       break;\r
4250 \r
4251     case IDM_LoadNextGame:\r
4252       ReloadGame(1);\r
4253       break;\r
4254 \r
4255     case IDM_LoadPrevGame:\r
4256       ReloadGame(-1);\r
4257       break;\r
4258 \r
4259     case IDM_ReloadGame:\r
4260       ReloadGame(0);\r
4261       break;\r
4262 \r
4263     case IDM_LoadPosition:\r
4264       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4265         Reset(FALSE, TRUE);\r
4266       }\r
4267       number = 1;\r
4268       f = OpenFileDialog(hwnd, "rb", "",\r
4269                          appData.oldSaveStyle ? "pos" : "fen",\r
4270                          POSITION_FILT,\r
4271                          "Load Position from File", &number, fileTitle, NULL);\r
4272       if (f != NULL) {\r
4273         LoadPosition(f, number, fileTitle);\r
4274       }\r
4275       break;\r
4276 \r
4277     case IDM_LoadNextPosition:\r
4278       ReloadPosition(1);\r
4279       break;\r
4280 \r
4281     case IDM_LoadPrevPosition:\r
4282       ReloadPosition(-1);\r
4283       break;\r
4284 \r
4285     case IDM_ReloadPosition:\r
4286       ReloadPosition(0);\r
4287       break;\r
4288 \r
4289     case IDM_SaveGame:\r
4290       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4291       f = OpenFileDialog(hwnd, "a", defName,\r
4292                          appData.oldSaveStyle ? "gam" : "pgn",\r
4293                          GAME_FILT,\r
4294                          "Save Game to File", NULL, fileTitle, NULL);\r
4295       if (f != NULL) {\r
4296         SaveGame(f, 0, "");\r
4297       }\r
4298       break;\r
4299 \r
4300     case IDM_SavePosition:\r
4301       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4302       f = OpenFileDialog(hwnd, "a", defName,\r
4303                          appData.oldSaveStyle ? "pos" : "fen",\r
4304                          POSITION_FILT,\r
4305                          "Save Position to File", NULL, fileTitle, NULL);\r
4306       if (f != NULL) {\r
4307         SavePosition(f, 0, "");\r
4308       }\r
4309       break;\r
4310 \r
4311     case IDM_SaveDiagram:\r
4312       defName = "diagram";\r
4313       f = OpenFileDialog(hwnd, "wb", defName,\r
4314                          "bmp",\r
4315                          DIAGRAM_FILT,\r
4316                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4317       if (f != NULL) {\r
4318         SaveDiagram(f);\r
4319       }\r
4320       break;\r
4321 \r
4322     case IDM_CopyGame:\r
4323       CopyGameToClipboard();\r
4324       break;\r
4325 \r
4326     case IDM_PasteGame:\r
4327       PasteGameFromClipboard();\r
4328       break;\r
4329 \r
4330     case IDM_CopyGameListToClipboard:\r
4331       CopyGameListToClipboard();\r
4332       break;\r
4333 \r
4334     /* [AS] Autodetect FEN or PGN data */\r
4335     case IDM_PasteAny:\r
4336       PasteGameOrFENFromClipboard();\r
4337       break;\r
4338 \r
4339     /* [AS] Move history */\r
4340     case IDM_ShowMoveHistory:\r
4341         if( MoveHistoryIsUp() ) {\r
4342             MoveHistoryPopDown();\r
4343         }\r
4344         else {\r
4345             MoveHistoryPopUp();\r
4346         }\r
4347         break;\r
4348 \r
4349     /* [AS] Eval graph */\r
4350     case IDM_ShowEvalGraph:\r
4351         if( EvalGraphIsUp() ) {\r
4352             EvalGraphPopDown();\r
4353         }\r
4354         else {\r
4355             EvalGraphPopUp();\r
4356             SetFocus(hwndMain);\r
4357         }\r
4358         break;\r
4359 \r
4360     /* [AS] Engine output */\r
4361     case IDM_ShowEngineOutput:\r
4362         if( EngineOutputIsUp() ) {\r
4363             EngineOutputPopDown();\r
4364         }\r
4365         else {\r
4366             EngineOutputPopUp();\r
4367         }\r
4368         break;\r
4369 \r
4370     /* [AS] User adjudication */\r
4371     case IDM_UserAdjudication_White:\r
4372         UserAdjudicationEvent( +1 );\r
4373         break;\r
4374 \r
4375     case IDM_UserAdjudication_Black:\r
4376         UserAdjudicationEvent( -1 );\r
4377         break;\r
4378 \r
4379     case IDM_UserAdjudication_Draw:\r
4380         UserAdjudicationEvent( 0 );\r
4381         break;\r
4382 \r
4383     /* [AS] Game list options dialog */\r
4384     case IDM_GameListOptions:\r
4385       GameListOptions();\r
4386       break;\r
4387 \r
4388     case IDM_NewChat:\r
4389       ChatPopUp();\r
4390       break;\r
4391 \r
4392     case IDM_CopyPosition:\r
4393       CopyFENToClipboard();\r
4394       break;\r
4395 \r
4396     case IDM_PastePosition:\r
4397       PasteFENFromClipboard();\r
4398       break;\r
4399 \r
4400     case IDM_MailMove:\r
4401       MailMoveEvent();\r
4402       break;\r
4403 \r
4404     case IDM_ReloadCMailMsg:\r
4405       Reset(TRUE, TRUE);\r
4406       ReloadCmailMsgEvent(FALSE);\r
4407       break;\r
4408 \r
4409     case IDM_Minimize:\r
4410       ShowWindow(hwnd, SW_MINIMIZE);\r
4411       break;\r
4412 \r
4413     case IDM_Exit:\r
4414       ExitEvent(0);\r
4415       break;\r
4416 \r
4417     case IDM_MachineWhite:\r
4418       MachineWhiteEvent();\r
4419       /*\r
4420        * refresh the tags dialog only if it's visible\r
4421        */\r
4422       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4423           char *tags;\r
4424           tags = PGNTags(&gameInfo);\r
4425           TagsPopUp(tags, CmailMsg());\r
4426           free(tags);\r
4427       }\r
4428       SAY("computer starts playing white");\r
4429       break;\r
4430 \r
4431     case IDM_MachineBlack:\r
4432       MachineBlackEvent();\r
4433       /*\r
4434        * refresh the tags dialog only if it's visible\r
4435        */\r
4436       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4437           char *tags;\r
4438           tags = PGNTags(&gameInfo);\r
4439           TagsPopUp(tags, CmailMsg());\r
4440           free(tags);\r
4441       }\r
4442       SAY("computer starts playing black");\r
4443       break;\r
4444 \r
4445     case IDM_TwoMachines:\r
4446       TwoMachinesEvent();\r
4447       /*\r
4448        * refresh the tags dialog only if it's visible\r
4449        */\r
4450       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4451           char *tags;\r
4452           tags = PGNTags(&gameInfo);\r
4453           TagsPopUp(tags, CmailMsg());\r
4454           free(tags);\r
4455       }\r
4456       SAY("programs start playing each other");\r
4457       break;\r
4458 \r
4459     case IDM_AnalysisMode:\r
4460       if (!first.analysisSupport) {\r
4461         sprintf(buf, "%s does not support analysis", first.tidy);\r
4462         DisplayError(buf, 0);\r
4463       } else {\r
4464         SAY("analyzing current position");\r
4465         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4466         if (appData.icsActive) {\r
4467                if (gameMode != IcsObserving) {\r
4468                        sprintf(buf, "You are not observing a game");\r
4469                        DisplayError(buf, 0);\r
4470                        /* secure check */\r
4471                        if (appData.icsEngineAnalyze) {\r
4472                                if (appData.debugMode) \r
4473                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4474                                ExitAnalyzeMode();\r
4475                                ModeHighlight();\r
4476                                break;\r
4477                        }\r
4478                        break;\r
4479                } else {\r
4480                        /* if enable, user want disable icsEngineAnalyze */\r
4481                        if (appData.icsEngineAnalyze) {\r
4482                                ExitAnalyzeMode();\r
4483                                ModeHighlight();\r
4484                                break;\r
4485                        }\r
4486                        appData.icsEngineAnalyze = TRUE;\r
4487                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4488                }\r
4489         } \r
4490         if (!appData.showThinking) ToggleShowThinking();\r
4491         AnalyzeModeEvent();\r
4492       }\r
4493       break;\r
4494 \r
4495     case IDM_AnalyzeFile:\r
4496       if (!first.analysisSupport) {\r
4497         char buf[MSG_SIZ];\r
4498         sprintf(buf, "%s does not support analysis", first.tidy);\r
4499         DisplayError(buf, 0);\r
4500       } else {\r
4501         if (!appData.showThinking) ToggleShowThinking();\r
4502         AnalyzeFileEvent();\r
4503         LoadGameDialog(hwnd, "Analyze Game from File");\r
4504         AnalysisPeriodicEvent(1);\r
4505       }\r
4506       break;\r
4507 \r
4508     case IDM_IcsClient:\r
4509       IcsClientEvent();\r
4510       break;\r
4511 \r
4512     case IDM_EditGame:\r
4513       EditGameEvent();\r
4514       SAY("edit game");\r
4515       break;\r
4516 \r
4517     case IDM_EditPosition:\r
4518       EditPositionEvent();\r
4519       SAY("to set up a position type a FEN");\r
4520       break;\r
4521 \r
4522     case IDM_Training:\r
4523       TrainingEvent();\r
4524       break;\r
4525 \r
4526     case IDM_ShowGameList:\r
4527       ShowGameListProc();\r
4528       break;\r
4529 \r
4530     case IDM_EditTags:\r
4531       EditTagsProc();\r
4532       break;\r
4533 \r
4534     case IDM_EditComment:\r
4535       if (commentUp && editComment) {\r
4536         CommentPopDown();\r
4537       } else {\r
4538         EditCommentEvent();\r
4539       }\r
4540       break;\r
4541 \r
4542     case IDM_Pause:\r
4543       PauseEvent();\r
4544       break;\r
4545 \r
4546     case IDM_Accept:\r
4547       AcceptEvent();\r
4548       break;\r
4549 \r
4550     case IDM_Decline:\r
4551       DeclineEvent();\r
4552       break;\r
4553 \r
4554     case IDM_Rematch:\r
4555       RematchEvent();\r
4556       break;\r
4557 \r
4558     case IDM_CallFlag:\r
4559       CallFlagEvent();\r
4560       break;\r
4561 \r
4562     case IDM_Draw:\r
4563       DrawEvent();\r
4564       break;\r
4565 \r
4566     case IDM_Adjourn:\r
4567       AdjournEvent();\r
4568       break;\r
4569 \r
4570     case IDM_Abort:\r
4571       AbortEvent();\r
4572       break;\r
4573 \r
4574     case IDM_Resign:\r
4575       ResignEvent();\r
4576       break;\r
4577 \r
4578     case IDM_StopObserving:\r
4579       StopObservingEvent();\r
4580       break;\r
4581 \r
4582     case IDM_StopExamining:\r
4583       StopExaminingEvent();\r
4584       break;\r
4585 \r
4586     case IDM_TypeInMove:\r
4587       PopUpMoveDialog('\000');\r
4588       break;\r
4589 \r
4590     case IDM_TypeInName:\r
4591       PopUpNameDialog('\000');\r
4592       break;\r
4593 \r
4594     case IDM_Backward:\r
4595       BackwardEvent();\r
4596       SetFocus(hwndMain);\r
4597       break;\r
4598 \r
4599     JAWS_MENU_ITEMS\r
4600 \r
4601     case IDM_Forward:\r
4602       ForwardEvent();\r
4603       SetFocus(hwndMain);\r
4604       break;\r
4605 \r
4606     case IDM_ToStart:\r
4607       ToStartEvent();\r
4608       SetFocus(hwndMain);\r
4609       break;\r
4610 \r
4611     case IDM_ToEnd:\r
4612       ToEndEvent();\r
4613       SetFocus(hwndMain);\r
4614       break;\r
4615 \r
4616     case IDM_Revert:\r
4617       RevertEvent();\r
4618       break;\r
4619 \r
4620     case IDM_TruncateGame:\r
4621       TruncateGameEvent();\r
4622       break;\r
4623 \r
4624     case IDM_MoveNow:\r
4625       MoveNowEvent();\r
4626       break;\r
4627 \r
4628     case IDM_RetractMove:\r
4629       RetractMoveEvent();\r
4630       break;\r
4631 \r
4632     case IDM_FlipView:\r
4633       flipView = !flipView;\r
4634       DrawPosition(FALSE, NULL);\r
4635       break;\r
4636 \r
4637     case IDM_FlipClock:\r
4638       flipClock = !flipClock;\r
4639       DisplayBothClocks();\r
4640       DrawPosition(FALSE, NULL);\r
4641       break;\r
4642 \r
4643     case IDM_MuteSounds:\r
4644       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4645       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4646                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4647       break;\r
4648 \r
4649     case IDM_GeneralOptions:\r
4650       GeneralOptionsPopup(hwnd);\r
4651       DrawPosition(TRUE, NULL);\r
4652       break;\r
4653 \r
4654     case IDM_BoardOptions:\r
4655       BoardOptionsPopup(hwnd);\r
4656       break;\r
4657 \r
4658     case IDM_EnginePlayOptions:\r
4659       EnginePlayOptionsPopup(hwnd);\r
4660       break;\r
4661 \r
4662     case IDM_Engine1Options:\r
4663       EngineOptionsPopup(hwnd, &first);\r
4664       break;\r
4665 \r
4666     case IDM_Engine2Options:\r
4667       EngineOptionsPopup(hwnd, &second);\r
4668       break;\r
4669 \r
4670     case IDM_OptionsUCI:\r
4671       UciOptionsPopup(hwnd);\r
4672       break;\r
4673 \r
4674     case IDM_IcsOptions:\r
4675       IcsOptionsPopup(hwnd);\r
4676       break;\r
4677 \r
4678     case IDM_Fonts:\r
4679       FontsOptionsPopup(hwnd);\r
4680       break;\r
4681 \r
4682     case IDM_Sounds:\r
4683       SoundOptionsPopup(hwnd);\r
4684       break;\r
4685 \r
4686     case IDM_CommPort:\r
4687       CommPortOptionsPopup(hwnd);\r
4688       break;\r
4689 \r
4690     case IDM_LoadOptions:\r
4691       LoadOptionsPopup(hwnd);\r
4692       break;\r
4693 \r
4694     case IDM_SaveOptions:\r
4695       SaveOptionsPopup(hwnd);\r
4696       break;\r
4697 \r
4698     case IDM_TimeControl:\r
4699       TimeControlOptionsPopup(hwnd);\r
4700       break;\r
4701 \r
4702     case IDM_SaveSettings:\r
4703       SaveSettings(settingsFileName);\r
4704       break;\r
4705 \r
4706     case IDM_SaveSettingsOnExit:\r
4707       saveSettingsOnExit = !saveSettingsOnExit;\r
4708       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4709                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4710                                          MF_CHECKED : MF_UNCHECKED));\r
4711       break;\r
4712 \r
4713     case IDM_Hint:\r
4714       HintEvent();\r
4715       break;\r
4716 \r
4717     case IDM_Book:\r
4718       BookEvent();\r
4719       break;\r
4720 \r
4721     case IDM_AboutGame:\r
4722       AboutGameEvent();\r
4723       break;\r
4724 \r
4725     case IDM_Debug:\r
4726       appData.debugMode = !appData.debugMode;\r
4727       if (appData.debugMode) {\r
4728         char dir[MSG_SIZ];\r
4729         GetCurrentDirectory(MSG_SIZ, dir);\r
4730         SetCurrentDirectory(installDir);\r
4731         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4732         SetCurrentDirectory(dir);\r
4733         setbuf(debugFP, NULL);\r
4734       } else {\r
4735         fclose(debugFP);\r
4736         debugFP = NULL;\r
4737       }\r
4738       break;\r
4739 \r
4740     case IDM_HELPCONTENTS:\r
4741       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4742           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4743           MessageBox (GetFocus(),\r
4744                     "Unable to activate help",\r
4745                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4746       }\r
4747       break;\r
4748 \r
4749     case IDM_HELPSEARCH:\r
4750         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4751             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4752         MessageBox (GetFocus(),\r
4753                     "Unable to activate help",\r
4754                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4755       }\r
4756       break;\r
4757 \r
4758     case IDM_HELPHELP:\r
4759       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4760         MessageBox (GetFocus(),\r
4761                     "Unable to activate help",\r
4762                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4763       }\r
4764       break;\r
4765 \r
4766     case IDM_ABOUT:\r
4767       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4768       DialogBox(hInst, \r
4769         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4770         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4771       FreeProcInstance(lpProc);\r
4772       break;\r
4773 \r
4774     case IDM_DirectCommand1:\r
4775       AskQuestionEvent("Direct Command",\r
4776                        "Send to chess program:", "", "1");\r
4777       break;\r
4778     case IDM_DirectCommand2:\r
4779       AskQuestionEvent("Direct Command",\r
4780                        "Send to second chess program:", "", "2");\r
4781       break;\r
4782 \r
4783     case EP_WhitePawn:\r
4784       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4785       fromX = fromY = -1;\r
4786       break;\r
4787 \r
4788     case EP_WhiteKnight:\r
4789       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4790       fromX = fromY = -1;\r
4791       break;\r
4792 \r
4793     case EP_WhiteBishop:\r
4794       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4795       fromX = fromY = -1;\r
4796       break;\r
4797 \r
4798     case EP_WhiteRook:\r
4799       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4800       fromX = fromY = -1;\r
4801       break;\r
4802 \r
4803     case EP_WhiteQueen:\r
4804       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4805       fromX = fromY = -1;\r
4806       break;\r
4807 \r
4808     case EP_WhiteFerz:\r
4809       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4810       fromX = fromY = -1;\r
4811       break;\r
4812 \r
4813     case EP_WhiteWazir:\r
4814       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4815       fromX = fromY = -1;\r
4816       break;\r
4817 \r
4818     case EP_WhiteAlfil:\r
4819       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4820       fromX = fromY = -1;\r
4821       break;\r
4822 \r
4823     case EP_WhiteCannon:\r
4824       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4825       fromX = fromY = -1;\r
4826       break;\r
4827 \r
4828     case EP_WhiteCardinal:\r
4829       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4830       fromX = fromY = -1;\r
4831       break;\r
4832 \r
4833     case EP_WhiteMarshall:\r
4834       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4835       fromX = fromY = -1;\r
4836       break;\r
4837 \r
4838     case EP_WhiteKing:\r
4839       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4840       fromX = fromY = -1;\r
4841       break;\r
4842 \r
4843     case EP_BlackPawn:\r
4844       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4845       fromX = fromY = -1;\r
4846       break;\r
4847 \r
4848     case EP_BlackKnight:\r
4849       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4850       fromX = fromY = -1;\r
4851       break;\r
4852 \r
4853     case EP_BlackBishop:\r
4854       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4855       fromX = fromY = -1;\r
4856       break;\r
4857 \r
4858     case EP_BlackRook:\r
4859       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4860       fromX = fromY = -1;\r
4861       break;\r
4862 \r
4863     case EP_BlackQueen:\r
4864       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4865       fromX = fromY = -1;\r
4866       break;\r
4867 \r
4868     case EP_BlackFerz:\r
4869       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4870       fromX = fromY = -1;\r
4871       break;\r
4872 \r
4873     case EP_BlackWazir:\r
4874       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
4875       fromX = fromY = -1;\r
4876       break;\r
4877 \r
4878     case EP_BlackAlfil:\r
4879       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
4880       fromX = fromY = -1;\r
4881       break;\r
4882 \r
4883     case EP_BlackCannon:\r
4884       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
4885       fromX = fromY = -1;\r
4886       break;\r
4887 \r
4888     case EP_BlackCardinal:\r
4889       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
4890       fromX = fromY = -1;\r
4891       break;\r
4892 \r
4893     case EP_BlackMarshall:\r
4894       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
4895       fromX = fromY = -1;\r
4896       break;\r
4897 \r
4898     case EP_BlackKing:\r
4899       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
4900       fromX = fromY = -1;\r
4901       break;\r
4902 \r
4903     case EP_EmptySquare:\r
4904       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
4905       fromX = fromY = -1;\r
4906       break;\r
4907 \r
4908     case EP_ClearBoard:\r
4909       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
4910       fromX = fromY = -1;\r
4911       break;\r
4912 \r
4913     case EP_White:\r
4914       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
4915       fromX = fromY = -1;\r
4916       break;\r
4917 \r
4918     case EP_Black:\r
4919       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
4920       fromX = fromY = -1;\r
4921       break;\r
4922 \r
4923     case EP_Promote:\r
4924       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
4925       fromX = fromY = -1;\r
4926       break;\r
4927 \r
4928     case EP_Demote:\r
4929       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
4930       fromX = fromY = -1;\r
4931       break;\r
4932 \r
4933     case DP_Pawn:\r
4934       DropMenuEvent(WhitePawn, fromX, fromY);\r
4935       fromX = fromY = -1;\r
4936       break;\r
4937 \r
4938     case DP_Knight:\r
4939       DropMenuEvent(WhiteKnight, fromX, fromY);\r
4940       fromX = fromY = -1;\r
4941       break;\r
4942 \r
4943     case DP_Bishop:\r
4944       DropMenuEvent(WhiteBishop, fromX, fromY);\r
4945       fromX = fromY = -1;\r
4946       break;\r
4947 \r
4948     case DP_Rook:\r
4949       DropMenuEvent(WhiteRook, fromX, fromY);\r
4950       fromX = fromY = -1;\r
4951       break;\r
4952 \r
4953     case DP_Queen:\r
4954       DropMenuEvent(WhiteQueen, fromX, fromY);\r
4955       fromX = fromY = -1;\r
4956       break;\r
4957 \r
4958     default:\r
4959       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4960     }\r
4961     break;\r
4962 \r
4963   case WM_TIMER:\r
4964     switch (wParam) {\r
4965     case CLOCK_TIMER_ID:\r
4966       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
4967       clockTimerEvent = 0;\r
4968       DecrementClocks(); /* call into back end */\r
4969       break;\r
4970     case LOAD_GAME_TIMER_ID:\r
4971       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
4972       loadGameTimerEvent = 0;\r
4973       AutoPlayGameLoop(); /* call into back end */\r
4974       break;\r
4975     case ANALYSIS_TIMER_ID:\r
4976       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
4977                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
4978         AnalysisPeriodicEvent(0);\r
4979       } else {\r
4980         KillTimer(hwnd, analysisTimerEvent);\r
4981         analysisTimerEvent = 0;\r
4982       }\r
4983       break;\r
4984     case DELAYED_TIMER_ID:\r
4985       KillTimer(hwnd, delayedTimerEvent);\r
4986       delayedTimerEvent = 0;\r
4987       delayedTimerCallback();\r
4988       break;\r
4989     }\r
4990     break;\r
4991 \r
4992   case WM_USER_Input:\r
4993     InputEvent(hwnd, message, wParam, lParam);\r
4994     break;\r
4995 \r
4996   /* [AS] Also move "attached" child windows */\r
4997   case WM_WINDOWPOSCHANGING:\r
4998 \r
4999     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5000         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5001 \r
5002         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5003             /* Window is moving */\r
5004             RECT rcMain;\r
5005 \r
5006 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5007             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5008             rcMain.right  = wpMain.x + wpMain.width;\r
5009             rcMain.top    = wpMain.y;\r
5010             rcMain.bottom = wpMain.y + wpMain.height;\r
5011             \r
5012             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5013             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5014             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5015             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5016             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5017             wpMain.x = lpwp->x;\r
5018             wpMain.y = lpwp->y;\r
5019         }\r
5020     }\r
5021     break;\r
5022 \r
5023   /* [AS] Snapping */\r
5024   case WM_ENTERSIZEMOVE:\r
5025     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5026     if (hwnd == hwndMain) {\r
5027       doingSizing = TRUE;\r
5028       lastSizing = 0;\r
5029     }\r
5030     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5031     break;\r
5032 \r
5033   case WM_SIZING:\r
5034     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5035     if (hwnd == hwndMain) {\r
5036       lastSizing = wParam;\r
5037     }\r
5038     break;\r
5039 \r
5040   case WM_MOVING:\r
5041     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5042       return OnMoving( &sd, hwnd, wParam, lParam );\r
5043 \r
5044   case WM_EXITSIZEMOVE:\r
5045     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5046     if (hwnd == hwndMain) {\r
5047       RECT client;\r
5048       doingSizing = FALSE;\r
5049       InvalidateRect(hwnd, &boardRect, FALSE);\r
5050       GetClientRect(hwnd, &client);\r
5051       ResizeBoard(client.right, client.bottom, lastSizing);\r
5052       lastSizing = 0;\r
5053       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5054     }\r
5055     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5056     break;\r
5057 \r
5058   case WM_DESTROY: /* message: window being destroyed */\r
5059     PostQuitMessage(0);\r
5060     break;\r
5061 \r
5062   case WM_CLOSE:\r
5063     if (hwnd == hwndMain) {\r
5064       ExitEvent(0);\r
5065     }\r
5066     break;\r
5067 \r
5068   default:      /* Passes it on if unprocessed */\r
5069     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5070   }\r
5071   return 0;\r
5072 }\r
5073 \r
5074 /*---------------------------------------------------------------------------*\\r
5075  *\r
5076  * Misc utility routines\r
5077  *\r
5078 \*---------------------------------------------------------------------------*/\r
5079 \r
5080 /*\r
5081  * Decent random number generator, at least not as bad as Windows\r
5082  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5083  */\r
5084 unsigned int randstate;\r
5085 \r
5086 int\r
5087 myrandom(void)\r
5088 {\r
5089   randstate = randstate * 1664525 + 1013904223;\r
5090   return (int) randstate & 0x7fffffff;\r
5091 }\r
5092 \r
5093 void\r
5094 mysrandom(unsigned int seed)\r
5095 {\r
5096   randstate = seed;\r
5097 }\r
5098 \r
5099 \r
5100 /* \r
5101  * returns TRUE if user selects a different color, FALSE otherwise \r
5102  */\r
5103 \r
5104 BOOL\r
5105 ChangeColor(HWND hwnd, COLORREF *which)\r
5106 {\r
5107   static BOOL firstTime = TRUE;\r
5108   static DWORD customColors[16];\r
5109   CHOOSECOLOR cc;\r
5110   COLORREF newcolor;\r
5111   int i;\r
5112   ColorClass ccl;\r
5113 \r
5114   if (firstTime) {\r
5115     /* Make initial colors in use available as custom colors */\r
5116     /* Should we put the compiled-in defaults here instead? */\r
5117     i = 0;\r
5118     customColors[i++] = lightSquareColor & 0xffffff;\r
5119     customColors[i++] = darkSquareColor & 0xffffff;\r
5120     customColors[i++] = whitePieceColor & 0xffffff;\r
5121     customColors[i++] = blackPieceColor & 0xffffff;\r
5122     customColors[i++] = highlightSquareColor & 0xffffff;\r
5123     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5124 \r
5125     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5126       customColors[i++] = textAttribs[ccl].color;\r
5127     }\r
5128     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5129     firstTime = FALSE;\r
5130   }\r
5131 \r
5132   cc.lStructSize = sizeof(cc);\r
5133   cc.hwndOwner = hwnd;\r
5134   cc.hInstance = NULL;\r
5135   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5136   cc.lpCustColors = (LPDWORD) customColors;\r
5137   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5138 \r
5139   if (!ChooseColor(&cc)) return FALSE;\r
5140 \r
5141   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5142   if (newcolor == *which) return FALSE;\r
5143   *which = newcolor;\r
5144   return TRUE;\r
5145 \r
5146   /*\r
5147   InitDrawingColors();\r
5148   InvalidateRect(hwnd, &boardRect, FALSE);\r
5149   */\r
5150 }\r
5151 \r
5152 BOOLEAN\r
5153 MyLoadSound(MySound *ms)\r
5154 {\r
5155   BOOL ok = FALSE;\r
5156   struct stat st;\r
5157   FILE *f;\r
5158 \r
5159   if (ms->data) free(ms->data);\r
5160   ms->data = NULL;\r
5161 \r
5162   switch (ms->name[0]) {\r
5163   case NULLCHAR:\r
5164     /* Silence */\r
5165     ok = TRUE;\r
5166     break;\r
5167   case '$':\r
5168     /* System sound from Control Panel.  Don't preload here. */\r
5169     ok = TRUE;\r
5170     break;\r
5171   case '!':\r
5172     if (ms->name[1] == NULLCHAR) {\r
5173       /* "!" alone = silence */\r
5174       ok = TRUE;\r
5175     } else {\r
5176       /* Builtin wave resource.  Error if not found. */\r
5177       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5178       if (h == NULL) break;\r
5179       ms->data = (void *)LoadResource(hInst, h);\r
5180       if (h == NULL) break;\r
5181       ok = TRUE;\r
5182     }\r
5183     break;\r
5184   default:\r
5185     /* .wav file.  Error if not found. */\r
5186     f = fopen(ms->name, "rb");\r
5187     if (f == NULL) break;\r
5188     if (fstat(fileno(f), &st) < 0) break;\r
5189     ms->data = malloc(st.st_size);\r
5190     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5191     fclose(f);\r
5192     ok = TRUE;\r
5193     break;\r
5194   }\r
5195   if (!ok) {\r
5196     char buf[MSG_SIZ];\r
5197     sprintf(buf, "Error loading sound %s", ms->name);\r
5198     DisplayError(buf, GetLastError());\r
5199   }\r
5200   return ok;\r
5201 }\r
5202 \r
5203 BOOLEAN\r
5204 MyPlaySound(MySound *ms)\r
5205 {\r
5206   BOOLEAN ok = FALSE;\r
5207 \r
5208   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5209   switch (ms->name[0]) {\r
5210   case NULLCHAR:\r
5211         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5212     /* Silence */\r
5213     ok = TRUE;\r
5214     break;\r
5215   case '$':\r
5216     /* System sound from Control Panel (deprecated feature).\r
5217        "$" alone or an unset sound name gets default beep (still in use). */\r
5218     if (ms->name[1]) {\r
5219       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5220     }\r
5221     if (!ok) ok = MessageBeep(MB_OK);\r
5222     break; \r
5223   case '!':\r
5224     /* Builtin wave resource, or "!" alone for silence */\r
5225     if (ms->name[1]) {\r
5226       if (ms->data == NULL) return FALSE;\r
5227       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5228     } else {\r
5229       ok = TRUE;\r
5230     }\r
5231     break;\r
5232   default:\r
5233     /* .wav file.  Error if not found. */\r
5234     if (ms->data == NULL) return FALSE;\r
5235     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5236     break;\r
5237   }\r
5238   /* Don't print an error: this can happen innocently if the sound driver\r
5239      is busy; for instance, if another instance of WinBoard is playing\r
5240      a sound at about the same time. */\r
5241   return ok;\r
5242 }\r
5243 \r
5244 \r
5245 LRESULT CALLBACK\r
5246 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5247 {\r
5248   BOOL ok;\r
5249   OPENFILENAME *ofn;\r
5250   static UINT *number; /* gross that this is static */\r
5251 \r
5252   switch (message) {\r
5253   case WM_INITDIALOG: /* message: initialize dialog box */\r
5254     /* Center the dialog over the application window */\r
5255     ofn = (OPENFILENAME *) lParam;\r
5256     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5257       number = (UINT *) ofn->lCustData;\r
5258       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5259     } else {\r
5260       number = NULL;\r
5261     }\r
5262     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5263     return FALSE;  /* Allow for further processing */\r
5264 \r
5265   case WM_COMMAND:\r
5266     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5267       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5268     }\r
5269     return FALSE;  /* Allow for further processing */\r
5270   }\r
5271   return FALSE;\r
5272 }\r
5273 \r
5274 UINT APIENTRY\r
5275 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5276 {\r
5277   static UINT *number;\r
5278   OPENFILENAME *ofname;\r
5279   OFNOTIFY *ofnot;\r
5280   switch (uiMsg) {\r
5281   case WM_INITDIALOG:\r
5282     ofname = (OPENFILENAME *)lParam;\r
5283     number = (UINT *)(ofname->lCustData);\r
5284     break;\r
5285   case WM_NOTIFY:\r
5286     ofnot = (OFNOTIFY *)lParam;\r
5287     if (ofnot->hdr.code == CDN_FILEOK) {\r
5288       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5289     }\r
5290     break;\r
5291   }\r
5292   return 0;\r
5293 }\r
5294 \r
5295 \r
5296 FILE *\r
5297 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5298                char *nameFilt, char *dlgTitle, UINT *number,\r
5299                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5300 {\r
5301   OPENFILENAME openFileName;\r
5302   char buf1[MSG_SIZ];\r
5303   FILE *f;\r
5304 \r
5305   if (fileName == NULL) fileName = buf1;\r
5306   if (defName == NULL) {\r
5307     strcpy(fileName, "*.");\r
5308     strcat(fileName, defExt);\r
5309   } else {\r
5310     strcpy(fileName, defName);\r
5311   }\r
5312   if (fileTitle) strcpy(fileTitle, "");\r
5313   if (number) *number = 0;\r
5314 \r
5315   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5316   openFileName.hwndOwner         = hwnd;\r
5317   openFileName.hInstance         = (HANDLE) hInst;\r
5318   openFileName.lpstrFilter       = nameFilt;\r
5319   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5320   openFileName.nMaxCustFilter    = 0L;\r
5321   openFileName.nFilterIndex      = 1L;\r
5322   openFileName.lpstrFile         = fileName;\r
5323   openFileName.nMaxFile          = MSG_SIZ;\r
5324   openFileName.lpstrFileTitle    = fileTitle;\r
5325   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5326   openFileName.lpstrInitialDir   = NULL;\r
5327   openFileName.lpstrTitle        = dlgTitle;\r
5328   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5329     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5330     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5331     | (oldDialog ? 0 : OFN_EXPLORER);\r
5332   openFileName.nFileOffset       = 0;\r
5333   openFileName.nFileExtension    = 0;\r
5334   openFileName.lpstrDefExt       = defExt;\r
5335   openFileName.lCustData         = (LONG) number;\r
5336   openFileName.lpfnHook          = oldDialog ?\r
5337     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5338   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5339 \r
5340   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5341                         GetOpenFileName(&openFileName)) {\r
5342     /* open the file */\r
5343     f = fopen(openFileName.lpstrFile, write);\r
5344     if (f == NULL) {\r
5345       MessageBox(hwnd, "File open failed", NULL,\r
5346                  MB_OK|MB_ICONEXCLAMATION);\r
5347       return NULL;\r
5348     }\r
5349   } else {\r
5350     int err = CommDlgExtendedError();\r
5351     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5352     return FALSE;\r
5353   }\r
5354   return f;\r
5355 }\r
5356 \r
5357 \r
5358 \r
5359 VOID APIENTRY\r
5360 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5361 {\r
5362   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5363 \r
5364   /*\r
5365    * Get the first pop-up menu in the menu template. This is the\r
5366    * menu that TrackPopupMenu displays.\r
5367    */\r
5368   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5369 \r
5370   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5371 \r
5372   /*\r
5373    * TrackPopup uses screen coordinates, so convert the\r
5374    * coordinates of the mouse click to screen coordinates.\r
5375    */\r
5376   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5377 \r
5378   /* Draw and track the floating pop-up menu. */\r
5379   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5380                  pt.x, pt.y, 0, hwnd, NULL);\r
5381 \r
5382   /* Destroy the menu.*/\r
5383   DestroyMenu(hmenu);\r
5384 }\r
5385    \r
5386 typedef struct {\r
5387   HWND hDlg, hText;\r
5388   int sizeX, sizeY, newSizeX, newSizeY;\r
5389   HDWP hdwp;\r
5390 } ResizeEditPlusButtonsClosure;\r
5391 \r
5392 BOOL CALLBACK\r
5393 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5394 {\r
5395   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5396   RECT rect;\r
5397   POINT pt;\r
5398 \r
5399   if (hChild == cl->hText) return TRUE;\r
5400   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5401   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5402   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5403   ScreenToClient(cl->hDlg, &pt);\r
5404   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5405     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5406   return TRUE;\r
5407 }\r
5408 \r
5409 /* Resize a dialog that has a (rich) edit field filling most of\r
5410    the top, with a row of buttons below */\r
5411 VOID\r
5412 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5413 {\r
5414   RECT rectText;\r
5415   int newTextHeight, newTextWidth;\r
5416   ResizeEditPlusButtonsClosure cl;\r
5417   \r
5418   /*if (IsIconic(hDlg)) return;*/\r
5419   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5420   \r
5421   cl.hdwp = BeginDeferWindowPos(8);\r
5422 \r
5423   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5424   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5425   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5426   if (newTextHeight < 0) {\r
5427     newSizeY += -newTextHeight;\r
5428     newTextHeight = 0;\r
5429   }\r
5430   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5431     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5432 \r
5433   cl.hDlg = hDlg;\r
5434   cl.hText = hText;\r
5435   cl.sizeX = sizeX;\r
5436   cl.sizeY = sizeY;\r
5437   cl.newSizeX = newSizeX;\r
5438   cl.newSizeY = newSizeY;\r
5439   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5440 \r
5441   EndDeferWindowPos(cl.hdwp);\r
5442 }\r
5443 \r
5444 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5445 {\r
5446     RECT    rChild, rParent;\r
5447     int     wChild, hChild, wParent, hParent;\r
5448     int     wScreen, hScreen, xNew, yNew;\r
5449     HDC     hdc;\r
5450 \r
5451     /* Get the Height and Width of the child window */\r
5452     GetWindowRect (hwndChild, &rChild);\r
5453     wChild = rChild.right - rChild.left;\r
5454     hChild = rChild.bottom - rChild.top;\r
5455 \r
5456     /* Get the Height and Width of the parent window */\r
5457     GetWindowRect (hwndParent, &rParent);\r
5458     wParent = rParent.right - rParent.left;\r
5459     hParent = rParent.bottom - rParent.top;\r
5460 \r
5461     /* Get the display limits */\r
5462     hdc = GetDC (hwndChild);\r
5463     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5464     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5465     ReleaseDC(hwndChild, hdc);\r
5466 \r
5467     /* Calculate new X position, then adjust for screen */\r
5468     xNew = rParent.left + ((wParent - wChild) /2);\r
5469     if (xNew < 0) {\r
5470         xNew = 0;\r
5471     } else if ((xNew+wChild) > wScreen) {\r
5472         xNew = wScreen - wChild;\r
5473     }\r
5474 \r
5475     /* Calculate new Y position, then adjust for screen */\r
5476     if( mode == 0 ) {\r
5477         yNew = rParent.top  + ((hParent - hChild) /2);\r
5478     }\r
5479     else {\r
5480         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5481     }\r
5482 \r
5483     if (yNew < 0) {\r
5484         yNew = 0;\r
5485     } else if ((yNew+hChild) > hScreen) {\r
5486         yNew = hScreen - hChild;\r
5487     }\r
5488 \r
5489     /* Set it, and return */\r
5490     return SetWindowPos (hwndChild, NULL,\r
5491                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5492 }\r
5493 \r
5494 /* Center one window over another */\r
5495 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5496 {\r
5497     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5498 }\r
5499 \r
5500 /*---------------------------------------------------------------------------*\\r
5501  *\r
5502  * Startup Dialog functions\r
5503  *\r
5504 \*---------------------------------------------------------------------------*/\r
5505 void\r
5506 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5507 {\r
5508   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5509 \r
5510   while (*cd != NULL) {\r
5511     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5512     cd++;\r
5513   }\r
5514 }\r
5515 \r
5516 void\r
5517 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5518 {\r
5519   char buf1[MAX_ARG_LEN];\r
5520   int len;\r
5521 \r
5522   if (str[0] == '@') {\r
5523     FILE* f = fopen(str + 1, "r");\r
5524     if (f == NULL) {\r
5525       DisplayFatalError(str + 1, errno, 2);\r
5526       return;\r
5527     }\r
5528     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5529     fclose(f);\r
5530     buf1[len] = NULLCHAR;\r
5531     str = buf1;\r
5532   }\r
5533 \r
5534   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5535 \r
5536   for (;;) {\r
5537     char buf[MSG_SIZ];\r
5538     char *end = strchr(str, '\n');\r
5539     if (end == NULL) return;\r
5540     memcpy(buf, str, end - str);\r
5541     buf[end - str] = NULLCHAR;\r
5542     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5543     str = end + 1;\r
5544   }\r
5545 }\r
5546 \r
5547 void\r
5548 SetStartupDialogEnables(HWND hDlg)\r
5549 {\r
5550   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5551     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5552     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5553   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5554     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5555   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5556     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5557   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5558     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5559   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5560     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5561     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5562     IsDlgButtonChecked(hDlg, OPT_View));\r
5563 }\r
5564 \r
5565 char *\r
5566 QuoteForFilename(char *filename)\r
5567 {\r
5568   int dquote, space;\r
5569   dquote = strchr(filename, '"') != NULL;\r
5570   space = strchr(filename, ' ') != NULL;\r
5571   if (dquote || space) {\r
5572     if (dquote) {\r
5573       return "'";\r
5574     } else {\r
5575       return "\"";\r
5576     }\r
5577   } else {\r
5578     return "";\r
5579   }\r
5580 }\r
5581 \r
5582 VOID\r
5583 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5584 {\r
5585   char buf[MSG_SIZ];\r
5586   char *q;\r
5587 \r
5588   InitComboStringsFromOption(hwndCombo, nthnames);\r
5589   q = QuoteForFilename(nthcp);\r
5590   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5591   if (*nthdir != NULLCHAR) {\r
5592     q = QuoteForFilename(nthdir);\r
5593     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5594   }\r
5595   if (*nthcp == NULLCHAR) {\r
5596     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5597   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5598     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5599     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5600   }\r
5601 }\r
5602 \r
5603 LRESULT CALLBACK\r
5604 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5605 {\r
5606   char buf[MSG_SIZ];\r
5607   HANDLE hwndCombo;\r
5608   char *p;\r
5609 \r
5610   switch (message) {\r
5611   case WM_INITDIALOG:\r
5612     /* Center the dialog */\r
5613     CenterWindow (hDlg, GetDesktopWindow());\r
5614     /* Initialize the dialog items */\r
5615     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5616                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5617                   firstChessProgramNames);\r
5618     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5619                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5620                   secondChessProgramNames);\r
5621     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5622     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5623     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5624     if (*appData.icsHelper != NULLCHAR) {\r
5625       char *q = QuoteForFilename(appData.icsHelper);\r
5626       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5627     }\r
5628     if (*appData.icsHost == NULLCHAR) {\r
5629       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5630       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5631     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5632       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5633       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5634     }\r
5635 \r
5636     if (appData.icsActive) {\r
5637       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5638     }\r
5639     else if (appData.noChessProgram) {\r
5640       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5641     }\r
5642     else {\r
5643       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5644     }\r
5645 \r
5646     SetStartupDialogEnables(hDlg);\r
5647     return TRUE;\r
5648 \r
5649   case WM_COMMAND:\r
5650     switch (LOWORD(wParam)) {\r
5651     case IDOK:\r
5652       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5653         strcpy(buf, "/fcp=");\r
5654         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5655         p = buf;\r
5656         ParseArgs(StringGet, &p);\r
5657         strcpy(buf, "/scp=");\r
5658         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5659         p = buf;\r
5660         ParseArgs(StringGet, &p);\r
5661         appData.noChessProgram = FALSE;\r
5662         appData.icsActive = FALSE;\r
5663       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5664         strcpy(buf, "/ics /icshost=");\r
5665         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5666         p = buf;\r
5667         ParseArgs(StringGet, &p);\r
5668         if (appData.zippyPlay) {\r
5669           strcpy(buf, "/fcp=");\r
5670           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5671           p = buf;\r
5672           ParseArgs(StringGet, &p);\r
5673         }\r
5674       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5675         appData.noChessProgram = TRUE;\r
5676         appData.icsActive = FALSE;\r
5677       } else {\r
5678         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5679                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5680         return TRUE;\r
5681       }\r
5682       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5683         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5684         p = buf;\r
5685         ParseArgs(StringGet, &p);\r
5686       }\r
5687       EndDialog(hDlg, TRUE);\r
5688       return TRUE;\r
5689 \r
5690     case IDCANCEL:\r
5691       ExitEvent(0);\r
5692       return TRUE;\r
5693 \r
5694     case IDM_HELPCONTENTS:\r
5695       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5696         MessageBox (GetFocus(),\r
5697                     "Unable to activate help",\r
5698                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5699       }\r
5700       break;\r
5701 \r
5702     default:\r
5703       SetStartupDialogEnables(hDlg);\r
5704       break;\r
5705     }\r
5706     break;\r
5707   }\r
5708   return FALSE;\r
5709 }\r
5710 \r
5711 /*---------------------------------------------------------------------------*\\r
5712  *\r
5713  * About box dialog functions\r
5714  *\r
5715 \*---------------------------------------------------------------------------*/\r
5716 \r
5717 /* Process messages for "About" dialog box */\r
5718 LRESULT CALLBACK\r
5719 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5720 {\r
5721   switch (message) {\r
5722   case WM_INITDIALOG: /* message: initialize dialog box */\r
5723     /* Center the dialog over the application window */\r
5724     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5725     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5726     JAWS_COPYRIGHT\r
5727     return (TRUE);\r
5728 \r
5729   case WM_COMMAND: /* message: received a command */\r
5730     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5731         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5732       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5733       return (TRUE);\r
5734     }\r
5735     break;\r
5736   }\r
5737   return (FALSE);\r
5738 }\r
5739 \r
5740 /*---------------------------------------------------------------------------*\\r
5741  *\r
5742  * Comment Dialog functions\r
5743  *\r
5744 \*---------------------------------------------------------------------------*/\r
5745 \r
5746 LRESULT CALLBACK\r
5747 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5748 {\r
5749   static HANDLE hwndText = NULL;\r
5750   int len, newSizeX, newSizeY, flags;\r
5751   static int sizeX, sizeY;\r
5752   char *str;\r
5753   RECT rect;\r
5754   MINMAXINFO *mmi;\r
5755 \r
5756   switch (message) {\r
5757   case WM_INITDIALOG: /* message: initialize dialog box */\r
5758     /* Initialize the dialog items */\r
5759     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5760     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5761     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5762     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5763     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5764     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5765     SetWindowText(hDlg, commentTitle);\r
5766     if (editComment) {\r
5767       SetFocus(hwndText);\r
5768     } else {\r
5769       SetFocus(GetDlgItem(hDlg, IDOK));\r
5770     }\r
5771     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5772                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5773                 MAKELPARAM(FALSE, 0));\r
5774     /* Size and position the dialog */\r
5775     if (!commentDialog) {\r
5776       commentDialog = hDlg;\r
5777       flags = SWP_NOZORDER;\r
5778       GetClientRect(hDlg, &rect);\r
5779       sizeX = rect.right;\r
5780       sizeY = rect.bottom;\r
5781       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5782           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5783         WINDOWPLACEMENT wp;\r
5784         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5785         wp.length = sizeof(WINDOWPLACEMENT);\r
5786         wp.flags = 0;\r
5787         wp.showCmd = SW_SHOW;\r
5788         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5789         wp.rcNormalPosition.left = wpComment.x;\r
5790         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5791         wp.rcNormalPosition.top = wpComment.y;\r
5792         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5793         SetWindowPlacement(hDlg, &wp);\r
5794 \r
5795         GetClientRect(hDlg, &rect);\r
5796         newSizeX = rect.right;\r
5797         newSizeY = rect.bottom;\r
5798         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5799                               newSizeX, newSizeY);\r
5800         sizeX = newSizeX;\r
5801         sizeY = newSizeY;\r
5802       }\r
5803     }\r
5804     return FALSE;\r
5805 \r
5806   case WM_COMMAND: /* message: received a command */\r
5807     switch (LOWORD(wParam)) {\r
5808     case IDOK:\r
5809       if (editComment) {\r
5810         char *p, *q;\r
5811         /* Read changed options from the dialog box */\r
5812         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5813         len = GetWindowTextLength(hwndText);\r
5814         str = (char *) malloc(len + 1);\r
5815         GetWindowText(hwndText, str, len + 1);\r
5816         p = q = str;\r
5817         while (*q) {\r
5818           if (*q == '\r')\r
5819             q++;\r
5820           else\r
5821             *p++ = *q++;\r
5822         }\r
5823         *p = NULLCHAR;\r
5824         ReplaceComment(commentIndex, str);\r
5825         free(str);\r
5826       }\r
5827       CommentPopDown();\r
5828       return TRUE;\r
5829 \r
5830     case IDCANCEL:\r
5831     case OPT_CancelComment:\r
5832       CommentPopDown();\r
5833       return TRUE;\r
5834 \r
5835     case OPT_ClearComment:\r
5836       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5837       break;\r
5838 \r
5839     case OPT_EditComment:\r
5840       EditCommentEvent();\r
5841       return TRUE;\r
5842 \r
5843     default:\r
5844       break;\r
5845     }\r
5846     break;\r
5847 \r
5848   case WM_SIZE:\r
5849     newSizeX = LOWORD(lParam);\r
5850     newSizeY = HIWORD(lParam);\r
5851     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
5852     sizeX = newSizeX;\r
5853     sizeY = newSizeY;\r
5854     break;\r
5855 \r
5856   case WM_GETMINMAXINFO:\r
5857     /* Prevent resizing window too small */\r
5858     mmi = (MINMAXINFO *) lParam;\r
5859     mmi->ptMinTrackSize.x = 100;\r
5860     mmi->ptMinTrackSize.y = 100;\r
5861     break;\r
5862   }\r
5863   return FALSE;\r
5864 }\r
5865 \r
5866 VOID\r
5867 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
5868 {\r
5869   FARPROC lpProc;\r
5870   char *p, *q;\r
5871 \r
5872   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
5873 \r
5874   if (str == NULL) str = "";\r
5875   p = (char *) malloc(2 * strlen(str) + 2);\r
5876   q = p;\r
5877   while (*str) {\r
5878     if (*str == '\n') *q++ = '\r';\r
5879     *q++ = *str++;\r
5880   }\r
5881   *q = NULLCHAR;\r
5882   if (commentText != NULL) free(commentText);\r
5883 \r
5884   commentIndex = index;\r
5885   commentTitle = title;\r
5886   commentText = p;\r
5887   editComment = edit;\r
5888 \r
5889   if (commentDialog) {\r
5890     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
5891     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
5892   } else {\r
5893     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
5894     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
5895                  hwndMain, (DLGPROC)lpProc);\r
5896     FreeProcInstance(lpProc);\r
5897   }\r
5898   commentUp = TRUE;\r
5899 }\r
5900 \r
5901 \r
5902 /*---------------------------------------------------------------------------*\\r
5903  *\r
5904  * Type-in move dialog functions\r
5905  * \r
5906 \*---------------------------------------------------------------------------*/\r
5907 \r
5908 LRESULT CALLBACK\r
5909 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5910 {\r
5911   char move[MSG_SIZ];\r
5912   HWND hInput;\r
5913   ChessMove moveType;\r
5914   int fromX, fromY, toX, toY;\r
5915   char promoChar;\r
5916 \r
5917   switch (message) {\r
5918   case WM_INITDIALOG:\r
5919     move[0] = (char) lParam;\r
5920     move[1] = NULLCHAR;\r
5921     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5922     hInput = GetDlgItem(hDlg, OPT_Move);\r
5923     SetWindowText(hInput, move);\r
5924     SetFocus(hInput);\r
5925     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5926     return FALSE;\r
5927 \r
5928   case WM_COMMAND:\r
5929     switch (LOWORD(wParam)) {\r
5930     case IDOK:\r
5931       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
5932       { int n; Board board;\r
5933         // [HGM] FENedit\r
5934         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
5935                 EditPositionPasteFEN(move);\r
5936                 EndDialog(hDlg, TRUE);\r
5937                 return TRUE;\r
5938         }\r
5939         // [HGM] movenum: allow move number to be typed in any mode\r
5940         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
5941           ToNrEvent(2*n-1);\r
5942           EndDialog(hDlg, TRUE);\r
5943           return TRUE;\r
5944         }\r
5945       }\r
5946       if (gameMode != EditGame && currentMove != forwardMostMove && \r
5947         gameMode != Training) {\r
5948         DisplayMoveError("Displayed move is not current");\r
5949       } else {\r
5950 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
5951         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5952           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
5953         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
5954         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5955           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
5956           if (gameMode != Training)\r
5957               forwardMostMove = currentMove;\r
5958           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
5959         } else {\r
5960           DisplayMoveError("Could not parse move");\r
5961         }\r
5962       }\r
5963       EndDialog(hDlg, TRUE);\r
5964       return TRUE;\r
5965     case IDCANCEL:\r
5966       EndDialog(hDlg, FALSE);\r
5967       return TRUE;\r
5968     default:\r
5969       break;\r
5970     }\r
5971     break;\r
5972   }\r
5973   return FALSE;\r
5974 }\r
5975 \r
5976 VOID\r
5977 PopUpMoveDialog(char firstchar)\r
5978 {\r
5979     FARPROC lpProc;\r
5980     \r
5981     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
5982         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
5983         gameMode == AnalyzeMode || gameMode == EditGame || \r
5984         gameMode == EditPosition || gameMode == IcsExamining ||\r
5985         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
5986         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
5987                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
5988                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
5989         gameMode == Training) {\r
5990       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
5991       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
5992         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
5993       FreeProcInstance(lpProc);\r
5994     }\r
5995 }\r
5996 \r
5997 /*---------------------------------------------------------------------------*\\r
5998  *\r
5999  * Type-in name dialog functions\r
6000  * \r
6001 \*---------------------------------------------------------------------------*/\r
6002 \r
6003 LRESULT CALLBACK\r
6004 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6005 {\r
6006   char move[MSG_SIZ];\r
6007   HWND hInput;\r
6008 \r
6009   switch (message) {\r
6010   case WM_INITDIALOG:\r
6011     move[0] = (char) lParam;\r
6012     move[1] = NULLCHAR;\r
6013     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6014     hInput = GetDlgItem(hDlg, OPT_Name);\r
6015     SetWindowText(hInput, move);\r
6016     SetFocus(hInput);\r
6017     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6018     return FALSE;\r
6019 \r
6020   case WM_COMMAND:\r
6021     switch (LOWORD(wParam)) {\r
6022     case IDOK:\r
6023       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6024       appData.userName = strdup(move);\r
6025       SetUserLogo();\r
6026 \r
6027       EndDialog(hDlg, TRUE);\r
6028       return TRUE;\r
6029     case IDCANCEL:\r
6030       EndDialog(hDlg, FALSE);\r
6031       return TRUE;\r
6032     default:\r
6033       break;\r
6034     }\r
6035     break;\r
6036   }\r
6037   return FALSE;\r
6038 }\r
6039 \r
6040 VOID\r
6041 PopUpNameDialog(char firstchar)\r
6042 {\r
6043     FARPROC lpProc;\r
6044     \r
6045       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6046       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6047         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6048       FreeProcInstance(lpProc);\r
6049 }\r
6050 \r
6051 /*---------------------------------------------------------------------------*\\r
6052  *\r
6053  *  Error dialogs\r
6054  * \r
6055 \*---------------------------------------------------------------------------*/\r
6056 \r
6057 /* Nonmodal error box */\r
6058 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6059                              WPARAM wParam, LPARAM lParam);\r
6060 \r
6061 VOID\r
6062 ErrorPopUp(char *title, char *content)\r
6063 {\r
6064   FARPROC lpProc;\r
6065   char *p, *q;\r
6066   BOOLEAN modal = hwndMain == NULL;\r
6067 \r
6068   p = content;\r
6069   q = errorMessage;\r
6070   while (*p) {\r
6071     if (*p == '\n') {\r
6072       if (modal) {\r
6073         *q++ = ' ';\r
6074         p++;\r
6075       } else {\r
6076         *q++ = '\r';\r
6077         *q++ = *p++;\r
6078       }\r
6079     } else {\r
6080       *q++ = *p++;\r
6081     }\r
6082   }\r
6083   *q = NULLCHAR;\r
6084   strncpy(errorTitle, title, sizeof(errorTitle));\r
6085   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6086   \r
6087   if (modal) {\r
6088     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6089   } else {\r
6090     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6091     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6092                  hwndMain, (DLGPROC)lpProc);\r
6093     FreeProcInstance(lpProc);\r
6094   }\r
6095 }\r
6096 \r
6097 VOID\r
6098 ErrorPopDown()\r
6099 {\r
6100   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6101   if (errorDialog == NULL) return;\r
6102   DestroyWindow(errorDialog);\r
6103   errorDialog = NULL;\r
6104   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6105 }\r
6106 \r
6107 LRESULT CALLBACK\r
6108 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6109 {\r
6110   HANDLE hwndText;\r
6111   RECT rChild;\r
6112 \r
6113   switch (message) {\r
6114   case WM_INITDIALOG:\r
6115     GetWindowRect(hDlg, &rChild);\r
6116 \r
6117     /*\r
6118     SetWindowPos(hDlg, NULL, rChild.left,\r
6119       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6120       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6121     */\r
6122 \r
6123     /* \r
6124         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6125         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6126         and it doesn't work when you resize the dialog.\r
6127         For now, just give it a default position.\r
6128     */\r
6129     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6130 \r
6131     errorDialog = hDlg;\r
6132     SetWindowText(hDlg, errorTitle);\r
6133     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6134     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6135     return FALSE;\r
6136 \r
6137   case WM_COMMAND:\r
6138     switch (LOWORD(wParam)) {\r
6139     case IDOK:\r
6140     case IDCANCEL:\r
6141       if (errorDialog == hDlg) errorDialog = NULL;\r
6142       DestroyWindow(hDlg);\r
6143       return TRUE;\r
6144 \r
6145     default:\r
6146       break;\r
6147     }\r
6148     break;\r
6149   }\r
6150   return FALSE;\r
6151 }\r
6152 \r
6153 #ifdef GOTHIC\r
6154 HWND gothicDialog = NULL;\r
6155 \r
6156 LRESULT CALLBACK\r
6157 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6158 {\r
6159   HANDLE hwndText;\r
6160   RECT rChild;\r
6161   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6162 \r
6163   switch (message) {\r
6164   case WM_INITDIALOG:\r
6165     GetWindowRect(hDlg, &rChild);\r
6166 \r
6167     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6168                                                              SWP_NOZORDER);\r
6169 \r
6170     /* \r
6171         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6172         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6173         and it doesn't work when you resize the dialog.\r
6174         For now, just give it a default position.\r
6175     */\r
6176     gothicDialog = hDlg;\r
6177     SetWindowText(hDlg, errorTitle);\r
6178     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6179     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6180     return FALSE;\r
6181 \r
6182   case WM_COMMAND:\r
6183     switch (LOWORD(wParam)) {\r
6184     case IDOK:\r
6185     case IDCANCEL:\r
6186       if (errorDialog == hDlg) errorDialog = NULL;\r
6187       DestroyWindow(hDlg);\r
6188       return TRUE;\r
6189 \r
6190     default:\r
6191       break;\r
6192     }\r
6193     break;\r
6194   }\r
6195   return FALSE;\r
6196 }\r
6197 \r
6198 VOID\r
6199 GothicPopUp(char *title, VariantClass variant)\r
6200 {\r
6201   FARPROC lpProc;\r
6202   static char *lastTitle;\r
6203 \r
6204   strncpy(errorTitle, title, sizeof(errorTitle));\r
6205   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6206 \r
6207   if(lastTitle != title && gothicDialog != NULL) {\r
6208     DestroyWindow(gothicDialog);\r
6209     gothicDialog = NULL;\r
6210   }\r
6211   if(variant != VariantNormal && gothicDialog == NULL) {\r
6212     title = lastTitle;\r
6213     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6214     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6215                  hwndMain, (DLGPROC)lpProc);\r
6216     FreeProcInstance(lpProc);\r
6217   }\r
6218 }\r
6219 #endif\r
6220 \r
6221 /*---------------------------------------------------------------------------*\\r
6222  *\r
6223  *  Ics Interaction console functions\r
6224  *\r
6225 \*---------------------------------------------------------------------------*/\r
6226 \r
6227 #define HISTORY_SIZE 64\r
6228 static char *history[HISTORY_SIZE];\r
6229 int histIn = 0, histP = 0;\r
6230 \r
6231 VOID\r
6232 SaveInHistory(char *cmd)\r
6233 {\r
6234   if (history[histIn] != NULL) {\r
6235     free(history[histIn]);\r
6236     history[histIn] = NULL;\r
6237   }\r
6238   if (*cmd == NULLCHAR) return;\r
6239   history[histIn] = StrSave(cmd);\r
6240   histIn = (histIn + 1) % HISTORY_SIZE;\r
6241   if (history[histIn] != NULL) {\r
6242     free(history[histIn]);\r
6243     history[histIn] = NULL;\r
6244   }\r
6245   histP = histIn;\r
6246 }\r
6247 \r
6248 char *\r
6249 PrevInHistory(char *cmd)\r
6250 {\r
6251   int newhp;\r
6252   if (histP == histIn) {\r
6253     if (history[histIn] != NULL) free(history[histIn]);\r
6254     history[histIn] = StrSave(cmd);\r
6255   }\r
6256   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6257   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6258   histP = newhp;\r
6259   return history[histP];\r
6260 }\r
6261 \r
6262 char *\r
6263 NextInHistory()\r
6264 {\r
6265   if (histP == histIn) return NULL;\r
6266   histP = (histP + 1) % HISTORY_SIZE;\r
6267   return history[histP];   \r
6268 }\r
6269 \r
6270 HMENU\r
6271 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6272 {\r
6273   HMENU hmenu, h;\r
6274   int i = 0;\r
6275   hmenu = LoadMenu(hInst, "TextMenu");\r
6276   h = GetSubMenu(hmenu, 0);\r
6277   while (e->item) {\r
6278     if (strcmp(e->item, "-") == 0) {\r
6279       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6280     } else {\r
6281       if (e->item[0] == '|') {\r
6282         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6283                    IDM_CommandX + i, &e->item[1]);\r
6284       } else {\r
6285         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6286       }\r
6287     }\r
6288     e++;\r
6289     i++;\r
6290   } \r
6291   return hmenu;\r
6292 }\r
6293 \r
6294 WNDPROC consoleTextWindowProc;\r
6295 \r
6296 void\r
6297 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6298 {\r
6299   char buf[MSG_SIZ], name[MSG_SIZ];\r
6300   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6301   CHARRANGE sel;\r
6302 \r
6303   if (!getname) {\r
6304     SetWindowText(hInput, command);\r
6305     if (immediate) {\r
6306       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6307     } else {\r
6308       sel.cpMin = 999999;\r
6309       sel.cpMax = 999999;\r
6310       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6311       SetFocus(hInput);\r
6312     }\r
6313     return;\r
6314   }    \r
6315   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6316   if (sel.cpMin == sel.cpMax) {\r
6317     /* Expand to surrounding word */\r
6318     TEXTRANGE tr;\r
6319     do {\r
6320       tr.chrg.cpMax = sel.cpMin;\r
6321       tr.chrg.cpMin = --sel.cpMin;\r
6322       if (sel.cpMin < 0) break;\r
6323       tr.lpstrText = name;\r
6324       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6325     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6326     sel.cpMin++;\r
6327 \r
6328     do {\r
6329       tr.chrg.cpMin = sel.cpMax;\r
6330       tr.chrg.cpMax = ++sel.cpMax;\r
6331       tr.lpstrText = name;\r
6332       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6333     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6334     sel.cpMax--;\r
6335 \r
6336     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6337       MessageBeep(MB_ICONEXCLAMATION);\r
6338       return;\r
6339     }\r
6340     tr.chrg = sel;\r
6341     tr.lpstrText = name;\r
6342     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6343   } else {\r
6344     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6345       MessageBeep(MB_ICONEXCLAMATION);\r
6346       return;\r
6347     }\r
6348     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6349   }\r
6350   if (immediate) {\r
6351     sprintf(buf, "%s %s", command, name);\r
6352     SetWindowText(hInput, buf);\r
6353     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6354   } else {\r
6355     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6356     SetWindowText(hInput, buf);\r
6357     sel.cpMin = 999999;\r
6358     sel.cpMax = 999999;\r
6359     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6360     SetFocus(hInput);\r
6361   }\r
6362 }\r
6363 \r
6364 LRESULT CALLBACK \r
6365 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6366 {\r
6367   HWND hInput;\r
6368   CHARRANGE sel;\r
6369 \r
6370   switch (message) {\r
6371   case WM_KEYDOWN:\r
6372     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6373     switch (wParam) {\r
6374     case VK_PRIOR:\r
6375       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6376       return 0;\r
6377     case VK_NEXT:\r
6378       sel.cpMin = 999999;\r
6379       sel.cpMax = 999999;\r
6380       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6381       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6382       return 0;\r
6383     }\r
6384     break;\r
6385   case WM_CHAR:\r
6386    if(wParam != '\022') {\r
6387     if (wParam == '\t') {\r
6388       if (GetKeyState(VK_SHIFT) < 0) {\r
6389         /* shifted */\r
6390         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6391         if (buttonDesc[0].hwnd) {\r
6392           SetFocus(buttonDesc[0].hwnd);\r
6393         } else {\r
6394           SetFocus(hwndMain);\r
6395         }\r
6396       } else {\r
6397         /* unshifted */\r
6398         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6399       }\r
6400     } else {\r
6401       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6402       JAWS_DELETE( SetFocus(hInput); )\r
6403       SendMessage(hInput, message, wParam, lParam);\r
6404     }\r
6405     return 0;\r
6406    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6407   case WM_RBUTTONUP:\r
6408     if (GetKeyState(VK_SHIFT) & ~1) {\r
6409       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6410         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6411     } else {\r
6412       POINT pt;\r
6413       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6414       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6415       if (sel.cpMin == sel.cpMax) {\r
6416         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6417         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6418       }\r
6419       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6420         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6421       }\r
6422       pt.x = LOWORD(lParam);\r
6423       pt.y = HIWORD(lParam);\r
6424       MenuPopup(hwnd, pt, hmenu, -1);\r
6425     }\r
6426     return 0;\r
6427   case WM_PASTE:\r
6428     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6429     SetFocus(hInput);\r
6430     return SendMessage(hInput, message, wParam, lParam);\r
6431   case WM_MBUTTONDOWN:\r
6432     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6433   case WM_RBUTTONDOWN:\r
6434     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6435       /* Move selection here if it was empty */\r
6436       POINT pt;\r
6437       pt.x = LOWORD(lParam);\r
6438       pt.y = HIWORD(lParam);\r
6439       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6440       if (sel.cpMin == sel.cpMax) {\r
6441         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6442         sel.cpMax = sel.cpMin;\r
6443         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6444       }\r
6445       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6446     }\r
6447     return 0;\r
6448   case WM_COMMAND:\r
6449     switch (LOWORD(wParam)) {\r
6450     case IDM_QuickPaste:\r
6451       {\r
6452         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6453         if (sel.cpMin == sel.cpMax) {\r
6454           MessageBeep(MB_ICONEXCLAMATION);\r
6455           return 0;\r
6456         }\r
6457         SendMessage(hwnd, WM_COPY, 0, 0);\r
6458         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6459         SendMessage(hInput, WM_PASTE, 0, 0);\r
6460         SetFocus(hInput);\r
6461         return 0;\r
6462       }\r
6463     case IDM_Cut:\r
6464       SendMessage(hwnd, WM_CUT, 0, 0);\r
6465       return 0;\r
6466     case IDM_Paste:\r
6467       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6468       return 0;\r
6469     case IDM_Copy:\r
6470       SendMessage(hwnd, WM_COPY, 0, 0);\r
6471       return 0;\r
6472     default:\r
6473       {\r
6474         int i = LOWORD(wParam) - IDM_CommandX;\r
6475         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6476             icsTextMenuEntry[i].command != NULL) {\r
6477           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6478                    icsTextMenuEntry[i].getname,\r
6479                    icsTextMenuEntry[i].immediate);\r
6480           return 0;\r
6481         }\r
6482       }\r
6483       break;\r
6484     }\r
6485     break;\r
6486   }\r
6487   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6488 }\r
6489 \r
6490 WNDPROC consoleInputWindowProc;\r
6491 \r
6492 LRESULT CALLBACK\r
6493 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6494 {\r
6495   char buf[MSG_SIZ];\r
6496   char *p;\r
6497   static BOOL sendNextChar = FALSE;\r
6498   static BOOL quoteNextChar = FALSE;\r
6499   InputSource *is = consoleInputSource;\r
6500   CHARFORMAT cf;\r
6501   CHARRANGE sel;\r
6502 \r
6503   switch (message) {\r
6504   case WM_CHAR:\r
6505     if (!appData.localLineEditing || sendNextChar) {\r
6506       is->buf[0] = (CHAR) wParam;\r
6507       is->count = 1;\r
6508       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6509       sendNextChar = FALSE;\r
6510       return 0;\r
6511     }\r
6512     if (quoteNextChar) {\r
6513       buf[0] = (char) wParam;\r
6514       buf[1] = NULLCHAR;\r
6515       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6516       quoteNextChar = FALSE;\r
6517       return 0;\r
6518     }\r
6519     switch (wParam) {\r
6520     case '\r':   /* Enter key */\r
6521       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6522       if (consoleEcho) SaveInHistory(is->buf);\r
6523       is->buf[is->count++] = '\n';\r
6524       is->buf[is->count] = NULLCHAR;\r
6525       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6526       if (consoleEcho) {\r
6527         ConsoleOutput(is->buf, is->count, TRUE);\r
6528       } else if (appData.localLineEditing) {\r
6529         ConsoleOutput("\n", 1, TRUE);\r
6530       }\r
6531       /* fall thru */\r
6532     case '\033': /* Escape key */\r
6533       SetWindowText(hwnd, "");\r
6534       cf.cbSize = sizeof(CHARFORMAT);\r
6535       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6536       if (consoleEcho) {\r
6537         cf.crTextColor = textAttribs[ColorNormal].color;\r
6538       } else {\r
6539         cf.crTextColor = COLOR_ECHOOFF;\r
6540       }\r
6541       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6542       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6543       return 0;\r
6544     case '\t':   /* Tab key */\r
6545       if (GetKeyState(VK_SHIFT) < 0) {\r
6546         /* shifted */\r
6547         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6548       } else {\r
6549         /* unshifted */\r
6550         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6551         if (buttonDesc[0].hwnd) {\r
6552           SetFocus(buttonDesc[0].hwnd);\r
6553         } else {\r
6554           SetFocus(hwndMain);\r
6555         }\r
6556       }\r
6557       return 0;\r
6558     case '\023': /* Ctrl+S */\r
6559       sendNextChar = TRUE;\r
6560       return 0;\r
6561     case '\021': /* Ctrl+Q */\r
6562       quoteNextChar = TRUE;\r
6563       return 0;\r
6564     JAWS_REPLAY\r
6565     default:\r
6566       break;\r
6567     }\r
6568     break;\r
6569   case WM_KEYDOWN:\r
6570     switch (wParam) {\r
6571     case VK_UP:\r
6572       GetWindowText(hwnd, buf, MSG_SIZ);\r
6573       p = PrevInHistory(buf);\r
6574       if (p != NULL) {\r
6575         SetWindowText(hwnd, p);\r
6576         sel.cpMin = 999999;\r
6577         sel.cpMax = 999999;\r
6578         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6579         return 0;\r
6580       }\r
6581       break;\r
6582     case VK_DOWN:\r
6583       p = NextInHistory();\r
6584       if (p != NULL) {\r
6585         SetWindowText(hwnd, p);\r
6586         sel.cpMin = 999999;\r
6587         sel.cpMax = 999999;\r
6588         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6589         return 0;\r
6590       }\r
6591       break;\r
6592     case VK_HOME:\r
6593     case VK_END:\r
6594       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6595       /* fall thru */\r
6596     case VK_PRIOR:\r
6597     case VK_NEXT:\r
6598       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6599       return 0;\r
6600     }\r
6601     break;\r
6602   case WM_MBUTTONDOWN:\r
6603     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6604       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6605     break;\r
6606   case WM_RBUTTONUP:\r
6607     if (GetKeyState(VK_SHIFT) & ~1) {\r
6608       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6609         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6610     } else {\r
6611       POINT pt;\r
6612       HMENU hmenu;\r
6613       hmenu = LoadMenu(hInst, "InputMenu");\r
6614       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6615       if (sel.cpMin == sel.cpMax) {\r
6616         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6617         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6618       }\r
6619       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6620         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6621       }\r
6622       pt.x = LOWORD(lParam);\r
6623       pt.y = HIWORD(lParam);\r
6624       MenuPopup(hwnd, pt, hmenu, -1);\r
6625     }\r
6626     return 0;\r
6627   case WM_COMMAND:\r
6628     switch (LOWORD(wParam)) { \r
6629     case IDM_Undo:\r
6630       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6631       return 0;\r
6632     case IDM_SelectAll:\r
6633       sel.cpMin = 0;\r
6634       sel.cpMax = -1; /*999999?*/\r
6635       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6636       return 0;\r
6637     case IDM_Cut:\r
6638       SendMessage(hwnd, WM_CUT, 0, 0);\r
6639       return 0;\r
6640     case IDM_Paste:\r
6641       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6642       return 0;\r
6643     case IDM_Copy:\r
6644       SendMessage(hwnd, WM_COPY, 0, 0);\r
6645       return 0;\r
6646     }\r
6647     break;\r
6648   }\r
6649   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6650 }\r
6651 \r
6652 #define CO_MAX  100000\r
6653 #define CO_TRIM   1000\r
6654 \r
6655 LRESULT CALLBACK\r
6656 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6657 {\r
6658   static SnapData sd;\r
6659   HWND hText, hInput;\r
6660   RECT rect;\r
6661   static int sizeX, sizeY;\r
6662   int newSizeX, newSizeY;\r
6663   MINMAXINFO *mmi;\r
6664   WORD wMask;\r
6665 \r
6666   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6667   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6668 \r
6669   switch (message) {\r
6670   case WM_NOTIFY:\r
6671     if (((NMHDR*)lParam)->code == EN_LINK)\r
6672     {\r
6673       ENLINK *pLink = (ENLINK*)lParam;\r
6674       if (pLink->msg == WM_LBUTTONUP)\r
6675       {\r
6676         TEXTRANGE tr;\r
6677 \r
6678         tr.chrg = pLink->chrg;\r
6679         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
6680         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
6681         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
6682         free(tr.lpstrText);\r
6683       }\r
6684     }\r
6685     break;\r
6686   case WM_INITDIALOG: /* message: initialize dialog box */\r
6687     hwndConsole = hDlg;\r
6688     SetFocus(hInput);\r
6689     consoleTextWindowProc = (WNDPROC)\r
6690       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6691     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6692     consoleInputWindowProc = (WNDPROC)\r
6693       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6694     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6695     Colorize(ColorNormal, TRUE);\r
6696     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6697     ChangedConsoleFont();\r
6698     GetClientRect(hDlg, &rect);\r
6699     sizeX = rect.right;\r
6700     sizeY = rect.bottom;\r
6701     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
6702         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
6703       WINDOWPLACEMENT wp;\r
6704       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6705       wp.length = sizeof(WINDOWPLACEMENT);\r
6706       wp.flags = 0;\r
6707       wp.showCmd = SW_SHOW;\r
6708       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6709       wp.rcNormalPosition.left = wpConsole.x;\r
6710       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6711       wp.rcNormalPosition.top = wpConsole.y;\r
6712       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6713       SetWindowPlacement(hDlg, &wp);\r
6714     }\r
6715 \r
6716    // [HGM] Chessknight's change 2004-07-13\r
6717    else { /* Determine Defaults */\r
6718        WINDOWPLACEMENT wp;\r
6719        wpConsole.x = wpMain.width + 1;\r
6720        wpConsole.y = wpMain.y;\r
6721        wpConsole.width = screenWidth -  wpMain.width;\r
6722        wpConsole.height = wpMain.height;\r
6723        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6724        wp.length = sizeof(WINDOWPLACEMENT);\r
6725        wp.flags = 0;\r
6726        wp.showCmd = SW_SHOW;\r
6727        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6728        wp.rcNormalPosition.left = wpConsole.x;\r
6729        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6730        wp.rcNormalPosition.top = wpConsole.y;\r
6731        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6732        SetWindowPlacement(hDlg, &wp);\r
6733     }\r
6734 \r
6735    // Allow hText to highlight URLs and send notifications on them\r
6736    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
6737    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
6738    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
6739    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
6740 \r
6741     return FALSE;\r
6742 \r
6743   case WM_SETFOCUS:\r
6744     SetFocus(hInput);\r
6745     return 0;\r
6746 \r
6747   case WM_CLOSE:\r
6748     ExitEvent(0);\r
6749     /* not reached */\r
6750     break;\r
6751 \r
6752   case WM_SIZE:\r
6753     if (IsIconic(hDlg)) break;\r
6754     newSizeX = LOWORD(lParam);\r
6755     newSizeY = HIWORD(lParam);\r
6756     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6757       RECT rectText, rectInput;\r
6758       POINT pt;\r
6759       int newTextHeight, newTextWidth;\r
6760       GetWindowRect(hText, &rectText);\r
6761       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6762       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6763       if (newTextHeight < 0) {\r
6764         newSizeY += -newTextHeight;\r
6765         newTextHeight = 0;\r
6766       }\r
6767       SetWindowPos(hText, NULL, 0, 0,\r
6768         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6769       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6770       pt.x = rectInput.left;\r
6771       pt.y = rectInput.top + newSizeY - sizeY;\r
6772       ScreenToClient(hDlg, &pt);\r
6773       SetWindowPos(hInput, NULL, \r
6774         pt.x, pt.y, /* needs client coords */   \r
6775         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6776         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6777     }\r
6778     sizeX = newSizeX;\r
6779     sizeY = newSizeY;\r
6780     break;\r
6781 \r
6782   case WM_GETMINMAXINFO:\r
6783     /* Prevent resizing window too small */\r
6784     mmi = (MINMAXINFO *) lParam;\r
6785     mmi->ptMinTrackSize.x = 100;\r
6786     mmi->ptMinTrackSize.y = 100;\r
6787     break;\r
6788 \r
6789   /* [AS] Snapping */\r
6790   case WM_ENTERSIZEMOVE:\r
6791     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
6792 \r
6793   case WM_SIZING:\r
6794     return OnSizing( &sd, hDlg, wParam, lParam );\r
6795 \r
6796   case WM_MOVING:\r
6797     return OnMoving( &sd, hDlg, wParam, lParam );\r
6798 \r
6799   case WM_EXITSIZEMOVE:\r
6800         UpdateICSWidth(hText);\r
6801     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
6802   }\r
6803 \r
6804   return DefWindowProc(hDlg, message, wParam, lParam);\r
6805 }\r
6806 \r
6807 \r
6808 VOID\r
6809 ConsoleCreate()\r
6810 {\r
6811   HWND hCons;\r
6812   if (hwndConsole) return;\r
6813   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6814   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6815 }\r
6816 \r
6817 \r
6818 VOID\r
6819 ConsoleOutput(char* data, int length, int forceVisible)\r
6820 {\r
6821   HWND hText;\r
6822   int trim, exlen;\r
6823   char *p, *q;\r
6824   char buf[CO_MAX+1];\r
6825   POINT pEnd;\r
6826   RECT rect;\r
6827   static int delayLF = 0;\r
6828   CHARRANGE savesel, sel;\r
6829 \r
6830   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6831   p = data;\r
6832   q = buf;\r
6833   if (delayLF) {\r
6834     *q++ = '\r';\r
6835     *q++ = '\n';\r
6836     delayLF = 0;\r
6837   }\r
6838   while (length--) {\r
6839     if (*p == '\n') {\r
6840       if (*++p) {\r
6841         *q++ = '\r';\r
6842         *q++ = '\n';\r
6843       } else {\r
6844         delayLF = 1;\r
6845       }\r
6846     } else if (*p == '\007') {\r
6847        MyPlaySound(&sounds[(int)SoundBell]);\r
6848        p++;\r
6849     } else {\r
6850       *q++ = *p++;\r
6851     }\r
6852   }\r
6853   *q = NULLCHAR;\r
6854   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
6855   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6856   /* Save current selection */\r
6857   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
6858   exlen = GetWindowTextLength(hText);\r
6859   /* Find out whether current end of text is visible */\r
6860   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
6861   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
6862   /* Trim existing text if it's too long */\r
6863   if (exlen + (q - buf) > CO_MAX) {\r
6864     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
6865     sel.cpMin = 0;\r
6866     sel.cpMax = trim;\r
6867     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6868     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
6869     exlen -= trim;\r
6870     savesel.cpMin -= trim;\r
6871     savesel.cpMax -= trim;\r
6872     if (exlen < 0) exlen = 0;\r
6873     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
6874     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
6875   }\r
6876   /* Append the new text */\r
6877   sel.cpMin = exlen;\r
6878   sel.cpMax = exlen;\r
6879   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6880   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
6881   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
6882   if (forceVisible || exlen == 0 ||\r
6883       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
6884        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
6885     /* Scroll to make new end of text visible if old end of text\r
6886        was visible or new text is an echo of user typein */\r
6887     sel.cpMin = 9999999;\r
6888     sel.cpMax = 9999999;\r
6889     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6890     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6891     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
6892     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6893   }\r
6894   if (savesel.cpMax == exlen || forceVisible) {\r
6895     /* Move insert point to new end of text if it was at the old\r
6896        end of text or if the new text is an echo of user typein */\r
6897     sel.cpMin = 9999999;\r
6898     sel.cpMax = 9999999;\r
6899     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6900   } else {\r
6901     /* Restore previous selection */\r
6902     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
6903   }\r
6904   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6905 }\r
6906 \r
6907 /*---------*/\r
6908 \r
6909 \r
6910 void\r
6911 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
6912 {\r
6913   char buf[100];\r
6914   char *str;\r
6915   COLORREF oldFg, oldBg;\r
6916   HFONT oldFont;\r
6917   RECT rect;\r
6918 \r
6919   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
6920 \r
6921   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6922   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6923   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6924 \r
6925   rect.left = x;\r
6926   rect.right = x + squareSize;\r
6927   rect.top  = y;\r
6928   rect.bottom = y + squareSize;\r
6929   str = buf;\r
6930 \r
6931   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
6932                     + (rightAlign ? (squareSize*2)/3 : 0),\r
6933              y, ETO_CLIPPED|ETO_OPAQUE,\r
6934              &rect, str, strlen(str), NULL);\r
6935 \r
6936   (void) SetTextColor(hdc, oldFg);\r
6937   (void) SetBkColor(hdc, oldBg);\r
6938   (void) SelectObject(hdc, oldFont);\r
6939 }\r
6940 \r
6941 void\r
6942 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
6943               RECT *rect, char *color, char *flagFell)\r
6944 {\r
6945   char buf[100];\r
6946   char *str;\r
6947   COLORREF oldFg, oldBg;\r
6948   HFONT oldFont;\r
6949 \r
6950   if (appData.clockMode) {\r
6951     if (tinyLayout)\r
6952       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
6953     else\r
6954       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
6955     str = buf;\r
6956   } else {\r
6957     str = color;\r
6958   }\r
6959 \r
6960   if (highlight) {\r
6961     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6962     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6963   } else {\r
6964     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
6965     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
6966   }\r
6967   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6968 \r
6969   JAWS_SILENCE\r
6970 \r
6971   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6972              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
6973              rect, str, strlen(str), NULL);\r
6974   if(logoHeight > 0 && appData.clockMode) {\r
6975       RECT r;\r
6976       sprintf(buf, "%s %s", buf+7, flagFell);\r
6977       r.top = rect->top + logoHeight/2;\r
6978       r.left = rect->left;\r
6979       r.right = rect->right;\r
6980       r.bottom = rect->bottom;\r
6981       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6982                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
6983                  &r, str, strlen(str), NULL);\r
6984   }\r
6985   (void) SetTextColor(hdc, oldFg);\r
6986   (void) SetBkColor(hdc, oldBg);\r
6987   (void) SelectObject(hdc, oldFont);\r
6988 }\r
6989 \r
6990 \r
6991 int\r
6992 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
6993            OVERLAPPED *ovl)\r
6994 {\r
6995   int ok, err;\r
6996 \r
6997   /* [AS]  */\r
6998   if( count <= 0 ) {\r
6999     if (appData.debugMode) {\r
7000       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7001     }\r
7002 \r
7003     return ERROR_INVALID_USER_BUFFER;\r
7004   }\r
7005 \r
7006   ResetEvent(ovl->hEvent);\r
7007   ovl->Offset = ovl->OffsetHigh = 0;\r
7008   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7009   if (ok) {\r
7010     err = NO_ERROR;\r
7011   } else {\r
7012     err = GetLastError();\r
7013     if (err == ERROR_IO_PENDING) {\r
7014       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7015       if (ok)\r
7016         err = NO_ERROR;\r
7017       else\r
7018         err = GetLastError();\r
7019     }\r
7020   }\r
7021   return err;\r
7022 }\r
7023 \r
7024 int\r
7025 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7026             OVERLAPPED *ovl)\r
7027 {\r
7028   int ok, err;\r
7029 \r
7030   ResetEvent(ovl->hEvent);\r
7031   ovl->Offset = ovl->OffsetHigh = 0;\r
7032   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7033   if (ok) {\r
7034     err = NO_ERROR;\r
7035   } else {\r
7036     err = GetLastError();\r
7037     if (err == ERROR_IO_PENDING) {\r
7038       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7039       if (ok)\r
7040         err = NO_ERROR;\r
7041       else\r
7042         err = GetLastError();\r
7043     }\r
7044   }\r
7045   return err;\r
7046 }\r
7047 \r
7048 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7049 void CheckForInputBufferFull( InputSource * is )\r
7050 {\r
7051     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7052         /* Look for end of line */\r
7053         char * p = is->buf;\r
7054         \r
7055         while( p < is->next && *p != '\n' ) {\r
7056             p++;\r
7057         }\r
7058 \r
7059         if( p >= is->next ) {\r
7060             if (appData.debugMode) {\r
7061                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7062             }\r
7063 \r
7064             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7065             is->count = (DWORD) -1;\r
7066             is->next = is->buf;\r
7067         }\r
7068     }\r
7069 }\r
7070 \r
7071 DWORD\r
7072 InputThread(LPVOID arg)\r
7073 {\r
7074   InputSource *is;\r
7075   OVERLAPPED ovl;\r
7076 \r
7077   is = (InputSource *) arg;\r
7078   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7079   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7080   while (is->hThread != NULL) {\r
7081     is->error = DoReadFile(is->hFile, is->next,\r
7082                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7083                            &is->count, &ovl);\r
7084     if (is->error == NO_ERROR) {\r
7085       is->next += is->count;\r
7086     } else {\r
7087       if (is->error == ERROR_BROKEN_PIPE) {\r
7088         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7089         is->count = 0;\r
7090       } else {\r
7091         is->count = (DWORD) -1;\r
7092         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7093         break; \r
7094       }\r
7095     }\r
7096 \r
7097     CheckForInputBufferFull( is );\r
7098 \r
7099     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7100 \r
7101     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7102 \r
7103     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7104   }\r
7105 \r
7106   CloseHandle(ovl.hEvent);\r
7107   CloseHandle(is->hFile);\r
7108 \r
7109   if (appData.debugMode) {\r
7110     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7111   }\r
7112 \r
7113   return 0;\r
7114 }\r
7115 \r
7116 \r
7117 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7118 DWORD\r
7119 NonOvlInputThread(LPVOID arg)\r
7120 {\r
7121   InputSource *is;\r
7122   char *p, *q;\r
7123   int i;\r
7124   char prev;\r
7125 \r
7126   is = (InputSource *) arg;\r
7127   while (is->hThread != NULL) {\r
7128     is->error = ReadFile(is->hFile, is->next,\r
7129                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7130                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7131     if (is->error == NO_ERROR) {\r
7132       /* Change CRLF to LF */\r
7133       if (is->next > is->buf) {\r
7134         p = is->next - 1;\r
7135         i = is->count + 1;\r
7136       } else {\r
7137         p = is->next;\r
7138         i = is->count;\r
7139       }\r
7140       q = p;\r
7141       prev = NULLCHAR;\r
7142       while (i > 0) {\r
7143         if (prev == '\r' && *p == '\n') {\r
7144           *(q-1) = '\n';\r
7145           is->count--;\r
7146         } else { \r
7147           *q++ = *p;\r
7148         }\r
7149         prev = *p++;\r
7150         i--;\r
7151       }\r
7152       *q = NULLCHAR;\r
7153       is->next = q;\r
7154     } else {\r
7155       if (is->error == ERROR_BROKEN_PIPE) {\r
7156         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7157         is->count = 0; \r
7158       } else {\r
7159         is->count = (DWORD) -1;\r
7160       }\r
7161     }\r
7162 \r
7163     CheckForInputBufferFull( is );\r
7164 \r
7165     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7166 \r
7167     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7168 \r
7169     if (is->count < 0) break;  /* Quit on error */\r
7170   }\r
7171   CloseHandle(is->hFile);\r
7172   return 0;\r
7173 }\r
7174 \r
7175 DWORD\r
7176 SocketInputThread(LPVOID arg)\r
7177 {\r
7178   InputSource *is;\r
7179 \r
7180   is = (InputSource *) arg;\r
7181   while (is->hThread != NULL) {\r
7182     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7183     if ((int)is->count == SOCKET_ERROR) {\r
7184       is->count = (DWORD) -1;\r
7185       is->error = WSAGetLastError();\r
7186     } else {\r
7187       is->error = NO_ERROR;\r
7188       is->next += is->count;\r
7189       if (is->count == 0 && is->second == is) {\r
7190         /* End of file on stderr; quit with no message */\r
7191         break;\r
7192       }\r
7193     }\r
7194     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7195 \r
7196     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7197 \r
7198     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7199   }\r
7200   return 0;\r
7201 }\r
7202 \r
7203 VOID\r
7204 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7205 {\r
7206   InputSource *is;\r
7207 \r
7208   is = (InputSource *) lParam;\r
7209   if (is->lineByLine) {\r
7210     /* Feed in lines one by one */\r
7211     char *p = is->buf;\r
7212     char *q = p;\r
7213     while (q < is->next) {\r
7214       if (*q++ == '\n') {\r
7215         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7216         p = q;\r
7217       }\r
7218     }\r
7219     \r
7220     /* Move any partial line to the start of the buffer */\r
7221     q = is->buf;\r
7222     while (p < is->next) {\r
7223       *q++ = *p++;\r
7224     }\r
7225     is->next = q;\r
7226 \r
7227     if (is->error != NO_ERROR || is->count == 0) {\r
7228       /* Notify backend of the error.  Note: If there was a partial\r
7229          line at the end, it is not flushed through. */\r
7230       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7231     }\r
7232   } else {\r
7233     /* Feed in the whole chunk of input at once */\r
7234     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7235     is->next = is->buf;\r
7236   }\r
7237 }\r
7238 \r
7239 /*---------------------------------------------------------------------------*\\r
7240  *\r
7241  *  Menu enables. Used when setting various modes.\r
7242  *\r
7243 \*---------------------------------------------------------------------------*/\r
7244 \r
7245 typedef struct {\r
7246   int item;\r
7247   int flags;\r
7248 } Enables;\r
7249 \r
7250 VOID\r
7251 GreyRevert(Boolean grey)\r
7252 { // [HGM] vari: for retracting variations in local mode\r
7253   HMENU hmenu = GetMenu(hwndMain);\r
7254   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7255 }\r
7256 \r
7257 VOID\r
7258 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7259 {\r
7260   while (enab->item > 0) {\r
7261     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7262     enab++;\r
7263   }\r
7264 }\r
7265 \r
7266 Enables gnuEnables[] = {\r
7267   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7268   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7269   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7270   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7271   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7272   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7273   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7274   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7275   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7276   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7277   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7278   { -1, -1 }\r
7279 };\r
7280 \r
7281 Enables icsEnables[] = {\r
7282   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7283   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7284   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7285   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7286   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7287   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7288   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7289   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7290   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7291   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7292   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7293   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7294   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7295   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7296   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7297   { -1, -1 }\r
7298 };\r
7299 \r
7300 #ifdef ZIPPY\r
7301 Enables zippyEnables[] = {\r
7302   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7303   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7304   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7305   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7306   { -1, -1 }\r
7307 };\r
7308 #endif\r
7309 \r
7310 Enables ncpEnables[] = {\r
7311   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7312   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7313   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7314   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7315   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7316   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7317   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7318   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7319   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7320   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7321   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7322   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7323   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7324   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7325   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7326   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7327   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7328   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7329   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7330   { -1, -1 }\r
7331 };\r
7332 \r
7333 Enables trainingOnEnables[] = {\r
7334   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7335   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7336   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7337   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7338   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7339   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7340   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7341   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7342   { -1, -1 }\r
7343 };\r
7344 \r
7345 Enables trainingOffEnables[] = {\r
7346   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7347   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7348   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7349   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7350   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7351   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7352   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7353   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7354   { -1, -1 }\r
7355 };\r
7356 \r
7357 /* These modify either ncpEnables or gnuEnables */\r
7358 Enables cmailEnables[] = {\r
7359   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7360   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7361   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7362   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7363   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7364   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7365   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7366   { -1, -1 }\r
7367 };\r
7368 \r
7369 Enables machineThinkingEnables[] = {\r
7370   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7371   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7372   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7373   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7374   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7375   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7376   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7377   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7378   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7379   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7380   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7381   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7382   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7383   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7384   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7385   { -1, -1 }\r
7386 };\r
7387 \r
7388 Enables userThinkingEnables[] = {\r
7389   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7390   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7391   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7392   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7393   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7394   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7395   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7396   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7397   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7398   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7399   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7400   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7401   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7402   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7403   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7404   { -1, -1 }\r
7405 };\r
7406 \r
7407 /*---------------------------------------------------------------------------*\\r
7408  *\r
7409  *  Front-end interface functions exported by XBoard.\r
7410  *  Functions appear in same order as prototypes in frontend.h.\r
7411  * \r
7412 \*---------------------------------------------------------------------------*/\r
7413 VOID\r
7414 ModeHighlight()\r
7415 {\r
7416   static UINT prevChecked = 0;\r
7417   static int prevPausing = 0;\r
7418   UINT nowChecked;\r
7419 \r
7420   if (pausing != prevPausing) {\r
7421     prevPausing = pausing;\r
7422     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7423                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7424     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7425   }\r
7426 \r
7427   switch (gameMode) {\r
7428   case BeginningOfGame:\r
7429     if (appData.icsActive)\r
7430       nowChecked = IDM_IcsClient;\r
7431     else if (appData.noChessProgram)\r
7432       nowChecked = IDM_EditGame;\r
7433     else\r
7434       nowChecked = IDM_MachineBlack;\r
7435     break;\r
7436   case MachinePlaysBlack:\r
7437     nowChecked = IDM_MachineBlack;\r
7438     break;\r
7439   case MachinePlaysWhite:\r
7440     nowChecked = IDM_MachineWhite;\r
7441     break;\r
7442   case TwoMachinesPlay:\r
7443     nowChecked = IDM_TwoMachines;\r
7444     break;\r
7445   case AnalyzeMode:\r
7446     nowChecked = IDM_AnalysisMode;\r
7447     break;\r
7448   case AnalyzeFile:\r
7449     nowChecked = IDM_AnalyzeFile;\r
7450     break;\r
7451   case EditGame:\r
7452     nowChecked = IDM_EditGame;\r
7453     break;\r
7454   case PlayFromGameFile:\r
7455     nowChecked = IDM_LoadGame;\r
7456     break;\r
7457   case EditPosition:\r
7458     nowChecked = IDM_EditPosition;\r
7459     break;\r
7460   case Training:\r
7461     nowChecked = IDM_Training;\r
7462     break;\r
7463   case IcsPlayingWhite:\r
7464   case IcsPlayingBlack:\r
7465   case IcsObserving:\r
7466   case IcsIdle:\r
7467     nowChecked = IDM_IcsClient;\r
7468     break;\r
7469   default:\r
7470   case EndOfGame:\r
7471     nowChecked = 0;\r
7472     break;\r
7473   }\r
7474   if (prevChecked != 0)\r
7475     (void) CheckMenuItem(GetMenu(hwndMain),\r
7476                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7477   if (nowChecked != 0)\r
7478     (void) CheckMenuItem(GetMenu(hwndMain),\r
7479                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7480 \r
7481   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7482     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7483                           MF_BYCOMMAND|MF_ENABLED);\r
7484   } else {\r
7485     (void) EnableMenuItem(GetMenu(hwndMain), \r
7486                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7487   }\r
7488 \r
7489   prevChecked = nowChecked;\r
7490 \r
7491   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7492   if (appData.icsActive) {\r
7493        if (appData.icsEngineAnalyze) {\r
7494                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7495                        MF_BYCOMMAND|MF_CHECKED);\r
7496        } else {\r
7497                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7498                        MF_BYCOMMAND|MF_UNCHECKED);\r
7499        }\r
7500   }\r
7501 }\r
7502 \r
7503 VOID\r
7504 SetICSMode()\r
7505 {\r
7506   HMENU hmenu = GetMenu(hwndMain);\r
7507   SetMenuEnables(hmenu, icsEnables);\r
7508   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7509     MF_BYPOSITION|MF_ENABLED);\r
7510 #ifdef ZIPPY\r
7511   if (appData.zippyPlay) {\r
7512     SetMenuEnables(hmenu, zippyEnables);\r
7513     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7514          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7515           MF_BYCOMMAND|MF_ENABLED);\r
7516   }\r
7517 #endif\r
7518 }\r
7519 \r
7520 VOID\r
7521 SetGNUMode()\r
7522 {\r
7523   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7524 }\r
7525 \r
7526 VOID\r
7527 SetNCPMode()\r
7528 {\r
7529   HMENU hmenu = GetMenu(hwndMain);\r
7530   SetMenuEnables(hmenu, ncpEnables);\r
7531   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7532     MF_BYPOSITION|MF_GRAYED);\r
7533     DrawMenuBar(hwndMain);\r
7534 }\r
7535 \r
7536 VOID\r
7537 SetCmailMode()\r
7538 {\r
7539   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7540 }\r
7541 \r
7542 VOID \r
7543 SetTrainingModeOn()\r
7544 {\r
7545   int i;\r
7546   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7547   for (i = 0; i < N_BUTTONS; i++) {\r
7548     if (buttonDesc[i].hwnd != NULL)\r
7549       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7550   }\r
7551   CommentPopDown();\r
7552 }\r
7553 \r
7554 VOID SetTrainingModeOff()\r
7555 {\r
7556   int i;\r
7557   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7558   for (i = 0; i < N_BUTTONS; i++) {\r
7559     if (buttonDesc[i].hwnd != NULL)\r
7560       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7561   }\r
7562 }\r
7563 \r
7564 \r
7565 VOID\r
7566 SetUserThinkingEnables()\r
7567 {\r
7568   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7569 }\r
7570 \r
7571 VOID\r
7572 SetMachineThinkingEnables()\r
7573 {\r
7574   HMENU hMenu = GetMenu(hwndMain);\r
7575   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7576 \r
7577   SetMenuEnables(hMenu, machineThinkingEnables);\r
7578 \r
7579   if (gameMode == MachinePlaysBlack) {\r
7580     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7581   } else if (gameMode == MachinePlaysWhite) {\r
7582     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7583   } else if (gameMode == TwoMachinesPlay) {\r
7584     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7585   }\r
7586 }\r
7587 \r
7588 \r
7589 VOID\r
7590 DisplayTitle(char *str)\r
7591 {\r
7592   char title[MSG_SIZ], *host;\r
7593   if (str[0] != NULLCHAR) {\r
7594     strcpy(title, str);\r
7595   } else if (appData.icsActive) {\r
7596     if (appData.icsCommPort[0] != NULLCHAR)\r
7597       host = "ICS";\r
7598     else \r
7599       host = appData.icsHost;\r
7600     sprintf(title, "%s: %s", szTitle, host);\r
7601   } else if (appData.noChessProgram) {\r
7602     strcpy(title, szTitle);\r
7603   } else {\r
7604     strcpy(title, szTitle);\r
7605     strcat(title, ": ");\r
7606     strcat(title, first.tidy);\r
7607   }\r
7608   SetWindowText(hwndMain, title);\r
7609 }\r
7610 \r
7611 \r
7612 VOID\r
7613 DisplayMessage(char *str1, char *str2)\r
7614 {\r
7615   HDC hdc;\r
7616   HFONT oldFont;\r
7617   int remain = MESSAGE_TEXT_MAX - 1;\r
7618   int len;\r
7619 \r
7620   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7621   messageText[0] = NULLCHAR;\r
7622   if (*str1) {\r
7623     len = strlen(str1);\r
7624     if (len > remain) len = remain;\r
7625     strncpy(messageText, str1, len);\r
7626     messageText[len] = NULLCHAR;\r
7627     remain -= len;\r
7628   }\r
7629   if (*str2 && remain >= 2) {\r
7630     if (*str1) {\r
7631       strcat(messageText, "  ");\r
7632       remain -= 2;\r
7633     }\r
7634     len = strlen(str2);\r
7635     if (len > remain) len = remain;\r
7636     strncat(messageText, str2, len);\r
7637   }\r
7638   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7639 \r
7640   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
7641 \r
7642   SAYMACHINEMOVE();\r
7643 \r
7644   hdc = GetDC(hwndMain);\r
7645   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7646   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7647              &messageRect, messageText, strlen(messageText), NULL);\r
7648   (void) SelectObject(hdc, oldFont);\r
7649   (void) ReleaseDC(hwndMain, hdc);\r
7650 }\r
7651 \r
7652 VOID\r
7653 DisplayError(char *str, int error)\r
7654 {\r
7655   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7656   int len;\r
7657 \r
7658   if (error == 0) {\r
7659     strcpy(buf, str);\r
7660   } else {\r
7661     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7662                         NULL, error, LANG_NEUTRAL,\r
7663                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7664     if (len > 0) {\r
7665       sprintf(buf, "%s:\n%s", str, buf2);\r
7666     } else {\r
7667       ErrorMap *em = errmap;\r
7668       while (em->err != 0 && em->err != error) em++;\r
7669       if (em->err != 0) {\r
7670         sprintf(buf, "%s:\n%s", str, em->msg);\r
7671       } else {\r
7672         sprintf(buf, "%s:\nError code %d", str, error);\r
7673       }\r
7674     }\r
7675   }\r
7676   \r
7677   ErrorPopUp("Error", buf);\r
7678 }\r
7679 \r
7680 \r
7681 VOID\r
7682 DisplayMoveError(char *str)\r
7683 {\r
7684   fromX = fromY = -1;\r
7685   ClearHighlights();\r
7686   DrawPosition(FALSE, NULL);\r
7687   if (appData.popupMoveErrors) {\r
7688     ErrorPopUp("Error", str);\r
7689   } else {\r
7690     DisplayMessage(str, "");\r
7691     moveErrorMessageUp = TRUE;\r
7692   }\r
7693 }\r
7694 \r
7695 VOID\r
7696 DisplayFatalError(char *str, int error, int exitStatus)\r
7697 {\r
7698   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7699   int len;\r
7700   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7701 \r
7702   if (error != 0) {\r
7703     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7704                         NULL, error, LANG_NEUTRAL,\r
7705                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7706     if (len > 0) {\r
7707       sprintf(buf, "%s:\n%s", str, buf2);\r
7708     } else {\r
7709       ErrorMap *em = errmap;\r
7710       while (em->err != 0 && em->err != error) em++;\r
7711       if (em->err != 0) {\r
7712         sprintf(buf, "%s:\n%s", str, em->msg);\r
7713       } else {\r
7714         sprintf(buf, "%s:\nError code %d", str, error);\r
7715       }\r
7716     }\r
7717     str = buf;\r
7718   }\r
7719   if (appData.debugMode) {\r
7720     fprintf(debugFP, "%s: %s\n", label, str);\r
7721   }\r
7722   if (appData.popupExitMessage) {\r
7723     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7724                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7725   }\r
7726   ExitEvent(exitStatus);\r
7727 }\r
7728 \r
7729 \r
7730 VOID\r
7731 DisplayInformation(char *str)\r
7732 {\r
7733   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7734 }\r
7735 \r
7736 \r
7737 VOID\r
7738 DisplayNote(char *str)\r
7739 {\r
7740   ErrorPopUp("Note", str);\r
7741 }\r
7742 \r
7743 \r
7744 typedef struct {\r
7745   char *title, *question, *replyPrefix;\r
7746   ProcRef pr;\r
7747 } QuestionParams;\r
7748 \r
7749 LRESULT CALLBACK\r
7750 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7751 {\r
7752   static QuestionParams *qp;\r
7753   char reply[MSG_SIZ];\r
7754   int len, err;\r
7755 \r
7756   switch (message) {\r
7757   case WM_INITDIALOG:\r
7758     qp = (QuestionParams *) lParam;\r
7759     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7760     SetWindowText(hDlg, qp->title);\r
7761     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7762     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7763     return FALSE;\r
7764 \r
7765   case WM_COMMAND:\r
7766     switch (LOWORD(wParam)) {\r
7767     case IDOK:\r
7768       strcpy(reply, qp->replyPrefix);\r
7769       if (*reply) strcat(reply, " ");\r
7770       len = strlen(reply);\r
7771       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7772       strcat(reply, "\n");\r
7773       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7774       EndDialog(hDlg, TRUE);\r
7775       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7776       return TRUE;\r
7777     case IDCANCEL:\r
7778       EndDialog(hDlg, FALSE);\r
7779       return TRUE;\r
7780     default:\r
7781       break;\r
7782     }\r
7783     break;\r
7784   }\r
7785   return FALSE;\r
7786 }\r
7787 \r
7788 VOID\r
7789 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7790 {\r
7791     QuestionParams qp;\r
7792     FARPROC lpProc;\r
7793     \r
7794     qp.title = title;\r
7795     qp.question = question;\r
7796     qp.replyPrefix = replyPrefix;\r
7797     qp.pr = pr;\r
7798     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7799     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7800       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7801     FreeProcInstance(lpProc);\r
7802 }\r
7803 \r
7804 /* [AS] Pick FRC position */\r
7805 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7806 {\r
7807     static int * lpIndexFRC;\r
7808     BOOL index_is_ok;\r
7809     char buf[16];\r
7810 \r
7811     switch( message )\r
7812     {\r
7813     case WM_INITDIALOG:\r
7814         lpIndexFRC = (int *) lParam;\r
7815 \r
7816         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7817 \r
7818         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
7819         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
7820         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
7821         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
7822 \r
7823         break;\r
7824 \r
7825     case WM_COMMAND:\r
7826         switch( LOWORD(wParam) ) {\r
7827         case IDOK:\r
7828             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7829             EndDialog( hDlg, 0 );\r
7830             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
7831             return TRUE;\r
7832         case IDCANCEL:\r
7833             EndDialog( hDlg, 1 );   \r
7834             return TRUE;\r
7835         case IDC_NFG_Edit:\r
7836             if( HIWORD(wParam) == EN_CHANGE ) {\r
7837                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7838 \r
7839                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
7840             }\r
7841             return TRUE;\r
7842         case IDC_NFG_Random:\r
7843             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
7844             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
7845             return TRUE;\r
7846         }\r
7847 \r
7848         break;\r
7849     }\r
7850 \r
7851     return FALSE;\r
7852 }\r
7853 \r
7854 int NewGameFRC()\r
7855 {\r
7856     int result;\r
7857     int index = appData.defaultFrcPosition;\r
7858     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
7859 \r
7860     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
7861 \r
7862     if( result == 0 ) {\r
7863         appData.defaultFrcPosition = index;\r
7864     }\r
7865 \r
7866     return result;\r
7867 }\r
7868 \r
7869 /* [AS] Game list options */\r
7870 typedef struct {\r
7871     char id;\r
7872     char * name;\r
7873 } GLT_Item;\r
7874 \r
7875 static GLT_Item GLT_ItemInfo[] = {\r
7876     { GLT_EVENT,      "Event" },\r
7877     { GLT_SITE,       "Site" },\r
7878     { GLT_DATE,       "Date" },\r
7879     { GLT_ROUND,      "Round" },\r
7880     { GLT_PLAYERS,    "Players" },\r
7881     { GLT_RESULT,     "Result" },\r
7882     { GLT_WHITE_ELO,  "White Rating" },\r
7883     { GLT_BLACK_ELO,  "Black Rating" },\r
7884     { GLT_TIME_CONTROL,"Time Control" },\r
7885     { GLT_VARIANT,    "Variant" },\r
7886     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
7887     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
7888     { 0, 0 }\r
7889 };\r
7890 \r
7891 const char * GLT_FindItem( char id )\r
7892 {\r
7893     const char * result = 0;\r
7894 \r
7895     GLT_Item * list = GLT_ItemInfo;\r
7896 \r
7897     while( list->id != 0 ) {\r
7898         if( list->id == id ) {\r
7899             result = list->name;\r
7900             break;\r
7901         }\r
7902 \r
7903         list++;\r
7904     }\r
7905 \r
7906     return result;\r
7907 }\r
7908 \r
7909 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
7910 {\r
7911     const char * name = GLT_FindItem( id );\r
7912 \r
7913     if( name != 0 ) {\r
7914         if( index >= 0 ) {\r
7915             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
7916         }\r
7917         else {\r
7918             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
7919         }\r
7920     }\r
7921 }\r
7922 \r
7923 void GLT_TagsToList( HWND hDlg, char * tags )\r
7924 {\r
7925     char * pc = tags;\r
7926 \r
7927     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
7928 \r
7929     while( *pc ) {\r
7930         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
7931         pc++;\r
7932     }\r
7933 \r
7934     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
7935 \r
7936     pc = GLT_ALL_TAGS;\r
7937 \r
7938     while( *pc ) {\r
7939         if( strchr( tags, *pc ) == 0 ) {\r
7940             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
7941         }\r
7942         pc++;\r
7943     }\r
7944 \r
7945     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
7946 }\r
7947 \r
7948 char GLT_ListItemToTag( HWND hDlg, int index )\r
7949 {\r
7950     char result = '\0';\r
7951     char name[128];\r
7952 \r
7953     GLT_Item * list = GLT_ItemInfo;\r
7954 \r
7955     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
7956         while( list->id != 0 ) {\r
7957             if( strcmp( list->name, name ) == 0 ) {\r
7958                 result = list->id;\r
7959                 break;\r
7960             }\r
7961 \r
7962             list++;\r
7963         }\r
7964     }\r
7965 \r
7966     return result;\r
7967 }\r
7968 \r
7969 void GLT_MoveSelection( HWND hDlg, int delta )\r
7970 {\r
7971     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
7972     int idx2 = idx1 + delta;\r
7973     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
7974 \r
7975     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
7976         char buf[128];\r
7977 \r
7978         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
7979         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
7980         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
7981         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
7982     }\r
7983 }\r
7984 \r
7985 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7986 {\r
7987     static char glt[64];\r
7988     static char * lpUserGLT;\r
7989 \r
7990     switch( message )\r
7991     {\r
7992     case WM_INITDIALOG:\r
7993         lpUserGLT = (char *) lParam;\r
7994         \r
7995         strcpy( glt, lpUserGLT );\r
7996 \r
7997         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7998 \r
7999         /* Initialize list */\r
8000         GLT_TagsToList( hDlg, glt );\r
8001 \r
8002         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8003 \r
8004         break;\r
8005 \r
8006     case WM_COMMAND:\r
8007         switch( LOWORD(wParam) ) {\r
8008         case IDOK:\r
8009             {\r
8010                 char * pc = lpUserGLT;\r
8011                 int idx = 0;\r
8012 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8013                 char id;\r
8014 \r
8015                 do {\r
8016                     id = GLT_ListItemToTag( hDlg, idx );\r
8017 \r
8018                     *pc++ = id;\r
8019                     idx++;\r
8020                 } while( id != '\0' );\r
8021             }\r
8022             EndDialog( hDlg, 0 );\r
8023             return TRUE;\r
8024         case IDCANCEL:\r
8025             EndDialog( hDlg, 1 );\r
8026             return TRUE;\r
8027 \r
8028         case IDC_GLT_Default:\r
8029             strcpy( glt, GLT_DEFAULT_TAGS );\r
8030             GLT_TagsToList( hDlg, glt );\r
8031             return TRUE;\r
8032 \r
8033         case IDC_GLT_Restore:\r
8034             strcpy( glt, lpUserGLT );\r
8035             GLT_TagsToList( hDlg, glt );\r
8036             return TRUE;\r
8037 \r
8038         case IDC_GLT_Up:\r
8039             GLT_MoveSelection( hDlg, -1 );\r
8040             return TRUE;\r
8041 \r
8042         case IDC_GLT_Down:\r
8043             GLT_MoveSelection( hDlg, +1 );\r
8044             return TRUE;\r
8045         }\r
8046 \r
8047         break;\r
8048     }\r
8049 \r
8050     return FALSE;\r
8051 }\r
8052 \r
8053 int GameListOptions()\r
8054 {\r
8055     char glt[64];\r
8056     int result;\r
8057     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8058 \r
8059     strcpy( glt, appData.gameListTags );\r
8060 \r
8061     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8062 \r
8063     if( result == 0 ) {\r
8064         /* [AS] Memory leak here! */\r
8065         appData.gameListTags = strdup( glt ); \r
8066     }\r
8067 \r
8068     return result;\r
8069 }\r
8070 \r
8071 \r
8072 VOID\r
8073 DisplayIcsInteractionTitle(char *str)\r
8074 {\r
8075   char consoleTitle[MSG_SIZ];\r
8076 \r
8077   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8078   SetWindowText(hwndConsole, consoleTitle);\r
8079 }\r
8080 \r
8081 void\r
8082 DrawPosition(int fullRedraw, Board board)\r
8083 {\r
8084   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8085 }\r
8086 \r
8087 void NotifyFrontendLogin()\r
8088 {\r
8089         if (hwndConsole)\r
8090                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8091 }\r
8092 \r
8093 VOID\r
8094 ResetFrontEnd()\r
8095 {\r
8096   fromX = fromY = -1;\r
8097   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8098     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8099     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8100     dragInfo.lastpos = dragInfo.pos;\r
8101     dragInfo.start.x = dragInfo.start.y = -1;\r
8102     dragInfo.from = dragInfo.start;\r
8103     ReleaseCapture();\r
8104     DrawPosition(TRUE, NULL);\r
8105   }\r
8106 }\r
8107 \r
8108 \r
8109 VOID\r
8110 CommentPopUp(char *title, char *str)\r
8111 {\r
8112   HWND hwnd = GetActiveWindow();\r
8113   EitherCommentPopUp(0, title, str, FALSE);\r
8114   SAY(str);\r
8115   SetActiveWindow(hwnd);\r
8116 }\r
8117 \r
8118 VOID\r
8119 CommentPopDown(void)\r
8120 {\r
8121   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8122   if (commentDialog) {\r
8123     ShowWindow(commentDialog, SW_HIDE);\r
8124   }\r
8125   commentUp = FALSE;\r
8126 }\r
8127 \r
8128 VOID\r
8129 EditCommentPopUp(int index, char *title, char *str)\r
8130 {\r
8131   EitherCommentPopUp(index, title, str, TRUE);\r
8132 }\r
8133 \r
8134 \r
8135 VOID\r
8136 RingBell()\r
8137 {\r
8138   MyPlaySound(&sounds[(int)SoundMove]);\r
8139 }\r
8140 \r
8141 VOID PlayIcsWinSound()\r
8142 {\r
8143   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8144 }\r
8145 \r
8146 VOID PlayIcsLossSound()\r
8147 {\r
8148   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8149 }\r
8150 \r
8151 VOID PlayIcsDrawSound()\r
8152 {\r
8153   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8154 }\r
8155 \r
8156 VOID PlayIcsUnfinishedSound()\r
8157 {\r
8158   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8159 }\r
8160 \r
8161 VOID\r
8162 PlayAlarmSound()\r
8163 {\r
8164   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8165 }\r
8166 \r
8167 \r
8168 VOID\r
8169 EchoOn()\r
8170 {\r
8171   HWND hInput;\r
8172   consoleEcho = TRUE;\r
8173   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8174   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8175   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8176 }\r
8177 \r
8178 \r
8179 VOID\r
8180 EchoOff()\r
8181 {\r
8182   CHARFORMAT cf;\r
8183   HWND hInput;\r
8184   consoleEcho = FALSE;\r
8185   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8186   /* This works OK: set text and background both to the same color */\r
8187   cf = consoleCF;\r
8188   cf.crTextColor = COLOR_ECHOOFF;\r
8189   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8190   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8191 }\r
8192 \r
8193 /* No Raw()...? */\r
8194 \r
8195 void Colorize(ColorClass cc, int continuation)\r
8196 {\r
8197   currentColorClass = cc;\r
8198   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8199   consoleCF.crTextColor = textAttribs[cc].color;\r
8200   consoleCF.dwEffects = textAttribs[cc].effects;\r
8201   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8202 }\r
8203 \r
8204 char *\r
8205 UserName()\r
8206 {\r
8207   static char buf[MSG_SIZ];\r
8208   DWORD bufsiz = MSG_SIZ;\r
8209 \r
8210   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8211         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8212   }\r
8213   if (!GetUserName(buf, &bufsiz)) {\r
8214     /*DisplayError("Error getting user name", GetLastError());*/\r
8215     strcpy(buf, "User");\r
8216   }\r
8217   return buf;\r
8218 }\r
8219 \r
8220 char *\r
8221 HostName()\r
8222 {\r
8223   static char buf[MSG_SIZ];\r
8224   DWORD bufsiz = MSG_SIZ;\r
8225 \r
8226   if (!GetComputerName(buf, &bufsiz)) {\r
8227     /*DisplayError("Error getting host name", GetLastError());*/\r
8228     strcpy(buf, "Unknown");\r
8229   }\r
8230   return buf;\r
8231 }\r
8232 \r
8233 \r
8234 int\r
8235 ClockTimerRunning()\r
8236 {\r
8237   return clockTimerEvent != 0;\r
8238 }\r
8239 \r
8240 int\r
8241 StopClockTimer()\r
8242 {\r
8243   if (clockTimerEvent == 0) return FALSE;\r
8244   KillTimer(hwndMain, clockTimerEvent);\r
8245   clockTimerEvent = 0;\r
8246   return TRUE;\r
8247 }\r
8248 \r
8249 void\r
8250 StartClockTimer(long millisec)\r
8251 {\r
8252   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8253                              (UINT) millisec, NULL);\r
8254 }\r
8255 \r
8256 void\r
8257 DisplayWhiteClock(long timeRemaining, int highlight)\r
8258 {\r
8259   HDC hdc;\r
8260   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8261 \r
8262   if(appData.noGUI) return;\r
8263   hdc = GetDC(hwndMain);\r
8264   if (!IsIconic(hwndMain)) {\r
8265     DisplayAClock(hdc, timeRemaining, highlight, \r
8266                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
8267   }\r
8268   if (highlight && iconCurrent == iconBlack) {\r
8269     iconCurrent = iconWhite;\r
8270     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8271     if (IsIconic(hwndMain)) {\r
8272       DrawIcon(hdc, 2, 2, iconCurrent);\r
8273     }\r
8274   }\r
8275   (void) ReleaseDC(hwndMain, hdc);\r
8276   if (hwndConsole)\r
8277     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8278 }\r
8279 \r
8280 void\r
8281 DisplayBlackClock(long timeRemaining, int highlight)\r
8282 {\r
8283   HDC hdc;\r
8284   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8285 \r
8286   if(appData.noGUI) return;\r
8287   hdc = GetDC(hwndMain);\r
8288   if (!IsIconic(hwndMain)) {\r
8289     DisplayAClock(hdc, timeRemaining, highlight, \r
8290                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8291   }\r
8292   if (highlight && iconCurrent == iconWhite) {\r
8293     iconCurrent = iconBlack;\r
8294     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8295     if (IsIconic(hwndMain)) {\r
8296       DrawIcon(hdc, 2, 2, iconCurrent);\r
8297     }\r
8298   }\r
8299   (void) ReleaseDC(hwndMain, hdc);\r
8300   if (hwndConsole)\r
8301     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8302 }\r
8303 \r
8304 \r
8305 int\r
8306 LoadGameTimerRunning()\r
8307 {\r
8308   return loadGameTimerEvent != 0;\r
8309 }\r
8310 \r
8311 int\r
8312 StopLoadGameTimer()\r
8313 {\r
8314   if (loadGameTimerEvent == 0) return FALSE;\r
8315   KillTimer(hwndMain, loadGameTimerEvent);\r
8316   loadGameTimerEvent = 0;\r
8317   return TRUE;\r
8318 }\r
8319 \r
8320 void\r
8321 StartLoadGameTimer(long millisec)\r
8322 {\r
8323   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8324                                 (UINT) millisec, NULL);\r
8325 }\r
8326 \r
8327 void\r
8328 AutoSaveGame()\r
8329 {\r
8330   char *defName;\r
8331   FILE *f;\r
8332   char fileTitle[MSG_SIZ];\r
8333 \r
8334   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8335   f = OpenFileDialog(hwndMain, "a", defName,\r
8336                      appData.oldSaveStyle ? "gam" : "pgn",\r
8337                      GAME_FILT, \r
8338                      "Save Game to File", NULL, fileTitle, NULL);\r
8339   if (f != NULL) {\r
8340     SaveGame(f, 0, "");\r
8341     fclose(f);\r
8342   }\r
8343 }\r
8344 \r
8345 \r
8346 void\r
8347 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8348 {\r
8349   if (delayedTimerEvent != 0) {\r
8350     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8351       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8352     }\r
8353     KillTimer(hwndMain, delayedTimerEvent);\r
8354     delayedTimerEvent = 0;\r
8355     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8356     delayedTimerCallback();\r
8357   }\r
8358   delayedTimerCallback = cb;\r
8359   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8360                                 (UINT) millisec, NULL);\r
8361 }\r
8362 \r
8363 DelayedEventCallback\r
8364 GetDelayedEvent()\r
8365 {\r
8366   if (delayedTimerEvent) {\r
8367     return delayedTimerCallback;\r
8368   } else {\r
8369     return NULL;\r
8370   }\r
8371 }\r
8372 \r
8373 void\r
8374 CancelDelayedEvent()\r
8375 {\r
8376   if (delayedTimerEvent) {\r
8377     KillTimer(hwndMain, delayedTimerEvent);\r
8378     delayedTimerEvent = 0;\r
8379   }\r
8380 }\r
8381 \r
8382 DWORD GetWin32Priority(int nice)\r
8383 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8384 /*\r
8385 REALTIME_PRIORITY_CLASS     0x00000100\r
8386 HIGH_PRIORITY_CLASS         0x00000080\r
8387 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8388 NORMAL_PRIORITY_CLASS       0x00000020\r
8389 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8390 IDLE_PRIORITY_CLASS         0x00000040\r
8391 */\r
8392         if (nice < -15) return 0x00000080;\r
8393         if (nice < 0)   return 0x00008000;\r
8394         if (nice == 0)  return 0x00000020;\r
8395         if (nice < 15)  return 0x00004000;\r
8396         return 0x00000040;\r
8397 }\r
8398 \r
8399 /* Start a child process running the given program.\r
8400    The process's standard output can be read from "from", and its\r
8401    standard input can be written to "to".\r
8402    Exit with fatal error if anything goes wrong.\r
8403    Returns an opaque pointer that can be used to destroy the process\r
8404    later.\r
8405 */\r
8406 int\r
8407 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8408 {\r
8409 #define BUFSIZE 4096\r
8410 \r
8411   HANDLE hChildStdinRd, hChildStdinWr,\r
8412     hChildStdoutRd, hChildStdoutWr;\r
8413   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8414   SECURITY_ATTRIBUTES saAttr;\r
8415   BOOL fSuccess;\r
8416   PROCESS_INFORMATION piProcInfo;\r
8417   STARTUPINFO siStartInfo;\r
8418   ChildProc *cp;\r
8419   char buf[MSG_SIZ];\r
8420   DWORD err;\r
8421 \r
8422   if (appData.debugMode) {\r
8423     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8424   }\r
8425 \r
8426   *pr = NoProc;\r
8427 \r
8428   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8429   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8430   saAttr.bInheritHandle = TRUE;\r
8431   saAttr.lpSecurityDescriptor = NULL;\r
8432 \r
8433   /*\r
8434    * The steps for redirecting child's STDOUT:\r
8435    *     1. Create anonymous pipe to be STDOUT for child.\r
8436    *     2. Create a noninheritable duplicate of read handle,\r
8437    *         and close the inheritable read handle.\r
8438    */\r
8439 \r
8440   /* Create a pipe for the child's STDOUT. */\r
8441   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8442     return GetLastError();\r
8443   }\r
8444 \r
8445   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8446   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8447                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8448                              FALSE,     /* not inherited */\r
8449                              DUPLICATE_SAME_ACCESS);\r
8450   if (! fSuccess) {\r
8451     return GetLastError();\r
8452   }\r
8453   CloseHandle(hChildStdoutRd);\r
8454 \r
8455   /*\r
8456    * The steps for redirecting child's STDIN:\r
8457    *     1. Create anonymous pipe to be STDIN for child.\r
8458    *     2. Create a noninheritable duplicate of write handle,\r
8459    *         and close the inheritable write handle.\r
8460    */\r
8461 \r
8462   /* Create a pipe for the child's STDIN. */\r
8463   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8464     return GetLastError();\r
8465   }\r
8466 \r
8467   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8468   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8469                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8470                              FALSE,     /* not inherited */\r
8471                              DUPLICATE_SAME_ACCESS);\r
8472   if (! fSuccess) {\r
8473     return GetLastError();\r
8474   }\r
8475   CloseHandle(hChildStdinWr);\r
8476 \r
8477   /* Arrange to (1) look in dir for the child .exe file, and\r
8478    * (2) have dir be the child's working directory.  Interpret\r
8479    * dir relative to the directory WinBoard loaded from. */\r
8480   GetCurrentDirectory(MSG_SIZ, buf);\r
8481   SetCurrentDirectory(installDir);\r
8482   SetCurrentDirectory(dir);\r
8483 \r
8484   /* Now create the child process. */\r
8485 \r
8486   siStartInfo.cb = sizeof(STARTUPINFO);\r
8487   siStartInfo.lpReserved = NULL;\r
8488   siStartInfo.lpDesktop = NULL;\r
8489   siStartInfo.lpTitle = NULL;\r
8490   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8491   siStartInfo.cbReserved2 = 0;\r
8492   siStartInfo.lpReserved2 = NULL;\r
8493   siStartInfo.hStdInput = hChildStdinRd;\r
8494   siStartInfo.hStdOutput = hChildStdoutWr;\r
8495   siStartInfo.hStdError = hChildStdoutWr;\r
8496 \r
8497   fSuccess = CreateProcess(NULL,\r
8498                            cmdLine,        /* command line */\r
8499                            NULL,           /* process security attributes */\r
8500                            NULL,           /* primary thread security attrs */\r
8501                            TRUE,           /* handles are inherited */\r
8502                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8503                            NULL,           /* use parent's environment */\r
8504                            NULL,\r
8505                            &siStartInfo, /* STARTUPINFO pointer */\r
8506                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8507 \r
8508   err = GetLastError();\r
8509   SetCurrentDirectory(buf); /* return to prev directory */\r
8510   if (! fSuccess) {\r
8511     return err;\r
8512   }\r
8513 \r
8514   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8515     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8516     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8517   }\r
8518 \r
8519   /* Close the handles we don't need in the parent */\r
8520   CloseHandle(piProcInfo.hThread);\r
8521   CloseHandle(hChildStdinRd);\r
8522   CloseHandle(hChildStdoutWr);\r
8523 \r
8524   /* Prepare return value */\r
8525   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8526   cp->kind = CPReal;\r
8527   cp->hProcess = piProcInfo.hProcess;\r
8528   cp->pid = piProcInfo.dwProcessId;\r
8529   cp->hFrom = hChildStdoutRdDup;\r
8530   cp->hTo = hChildStdinWrDup;\r
8531 \r
8532   *pr = (void *) cp;\r
8533 \r
8534   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8535      2000 where engines sometimes don't see the initial command(s)\r
8536      from WinBoard and hang.  I don't understand how that can happen,\r
8537      but the Sleep is harmless, so I've put it in.  Others have also\r
8538      reported what may be the same problem, so hopefully this will fix\r
8539      it for them too.  */\r
8540   Sleep(500);\r
8541 \r
8542   return NO_ERROR;\r
8543 }\r
8544 \r
8545 \r
8546 void\r
8547 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8548 {\r
8549   ChildProc *cp; int result;\r
8550 \r
8551   cp = (ChildProc *) pr;\r
8552   if (cp == NULL) return;\r
8553 \r
8554   switch (cp->kind) {\r
8555   case CPReal:\r
8556     /* TerminateProcess is considered harmful, so... */\r
8557     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8558     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8559     /* The following doesn't work because the chess program\r
8560        doesn't "have the same console" as WinBoard.  Maybe\r
8561        we could arrange for this even though neither WinBoard\r
8562        nor the chess program uses a console for stdio? */\r
8563     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8564 \r
8565     /* [AS] Special termination modes for misbehaving programs... */\r
8566     if( signal == 9 ) { \r
8567         result = TerminateProcess( cp->hProcess, 0 );\r
8568 \r
8569         if ( appData.debugMode) {\r
8570             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8571         }\r
8572     }\r
8573     else if( signal == 10 ) {\r
8574         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8575 \r
8576         if( dw != WAIT_OBJECT_0 ) {\r
8577             result = TerminateProcess( cp->hProcess, 0 );\r
8578 \r
8579             if ( appData.debugMode) {\r
8580                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8581             }\r
8582 \r
8583         }\r
8584     }\r
8585 \r
8586     CloseHandle(cp->hProcess);\r
8587     break;\r
8588 \r
8589   case CPComm:\r
8590     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8591     break;\r
8592 \r
8593   case CPSock:\r
8594     closesocket(cp->sock);\r
8595     WSACleanup();\r
8596     break;\r
8597 \r
8598   case CPRcmd:\r
8599     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8600     closesocket(cp->sock);\r
8601     closesocket(cp->sock2);\r
8602     WSACleanup();\r
8603     break;\r
8604   }\r
8605   free(cp);\r
8606 }\r
8607 \r
8608 void\r
8609 InterruptChildProcess(ProcRef pr)\r
8610 {\r
8611   ChildProc *cp;\r
8612 \r
8613   cp = (ChildProc *) pr;\r
8614   if (cp == NULL) return;\r
8615   switch (cp->kind) {\r
8616   case CPReal:\r
8617     /* The following doesn't work because the chess program\r
8618        doesn't "have the same console" as WinBoard.  Maybe\r
8619        we could arrange for this even though neither WinBoard\r
8620        nor the chess program uses a console for stdio */\r
8621     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8622     break;\r
8623 \r
8624   case CPComm:\r
8625   case CPSock:\r
8626     /* Can't interrupt */\r
8627     break;\r
8628 \r
8629   case CPRcmd:\r
8630     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8631     break;\r
8632   }\r
8633 }\r
8634 \r
8635 \r
8636 int\r
8637 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8638 {\r
8639   char cmdLine[MSG_SIZ];\r
8640 \r
8641   if (port[0] == NULLCHAR) {\r
8642     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8643   } else {\r
8644     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8645   }\r
8646   return StartChildProcess(cmdLine, "", pr);\r
8647 }\r
8648 \r
8649 \r
8650 /* Code to open TCP sockets */\r
8651 \r
8652 int\r
8653 OpenTCP(char *host, char *port, ProcRef *pr)\r
8654 {\r
8655   ChildProc *cp;\r
8656   int err;\r
8657   SOCKET s;\r
8658   struct sockaddr_in sa, mysa;\r
8659   struct hostent FAR *hp;\r
8660   unsigned short uport;\r
8661   WORD wVersionRequested;\r
8662   WSADATA wsaData;\r
8663 \r
8664   /* Initialize socket DLL */\r
8665   wVersionRequested = MAKEWORD(1, 1);\r
8666   err = WSAStartup(wVersionRequested, &wsaData);\r
8667   if (err != 0) return err;\r
8668 \r
8669   /* Make socket */\r
8670   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8671     err = WSAGetLastError();\r
8672     WSACleanup();\r
8673     return err;\r
8674   }\r
8675 \r
8676   /* Bind local address using (mostly) don't-care values.\r
8677    */\r
8678   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8679   mysa.sin_family = AF_INET;\r
8680   mysa.sin_addr.s_addr = INADDR_ANY;\r
8681   uport = (unsigned short) 0;\r
8682   mysa.sin_port = htons(uport);\r
8683   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8684       == SOCKET_ERROR) {\r
8685     err = WSAGetLastError();\r
8686     WSACleanup();\r
8687     return err;\r
8688   }\r
8689 \r
8690   /* Resolve remote host name */\r
8691   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8692   if (!(hp = gethostbyname(host))) {\r
8693     unsigned int b0, b1, b2, b3;\r
8694 \r
8695     err = WSAGetLastError();\r
8696 \r
8697     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8698       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8699       hp->h_addrtype = AF_INET;\r
8700       hp->h_length = 4;\r
8701       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8702       hp->h_addr_list[0] = (char *) malloc(4);\r
8703       hp->h_addr_list[0][0] = (char) b0;\r
8704       hp->h_addr_list[0][1] = (char) b1;\r
8705       hp->h_addr_list[0][2] = (char) b2;\r
8706       hp->h_addr_list[0][3] = (char) b3;\r
8707     } else {\r
8708       WSACleanup();\r
8709       return err;\r
8710     }\r
8711   }\r
8712   sa.sin_family = hp->h_addrtype;\r
8713   uport = (unsigned short) atoi(port);\r
8714   sa.sin_port = htons(uport);\r
8715   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8716 \r
8717   /* Make connection */\r
8718   if (connect(s, (struct sockaddr *) &sa,\r
8719               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8720     err = WSAGetLastError();\r
8721     WSACleanup();\r
8722     return err;\r
8723   }\r
8724 \r
8725   /* Prepare return value */\r
8726   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8727   cp->kind = CPSock;\r
8728   cp->sock = s;\r
8729   *pr = (ProcRef *) cp;\r
8730 \r
8731   return NO_ERROR;\r
8732 }\r
8733 \r
8734 int\r
8735 OpenCommPort(char *name, ProcRef *pr)\r
8736 {\r
8737   HANDLE h;\r
8738   COMMTIMEOUTS ct;\r
8739   ChildProc *cp;\r
8740   char fullname[MSG_SIZ];\r
8741 \r
8742   if (*name != '\\')\r
8743     sprintf(fullname, "\\\\.\\%s", name);\r
8744   else\r
8745     strcpy(fullname, name);\r
8746 \r
8747   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8748                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8749   if (h == (HANDLE) -1) {\r
8750     return GetLastError();\r
8751   }\r
8752   hCommPort = h;\r
8753 \r
8754   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8755 \r
8756   /* Accumulate characters until a 100ms pause, then parse */\r
8757   ct.ReadIntervalTimeout = 100;\r
8758   ct.ReadTotalTimeoutMultiplier = 0;\r
8759   ct.ReadTotalTimeoutConstant = 0;\r
8760   ct.WriteTotalTimeoutMultiplier = 0;\r
8761   ct.WriteTotalTimeoutConstant = 0;\r
8762   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8763 \r
8764   /* Prepare return value */\r
8765   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8766   cp->kind = CPComm;\r
8767   cp->hFrom = h;\r
8768   cp->hTo = h;\r
8769   *pr = (ProcRef *) cp;\r
8770 \r
8771   return NO_ERROR;\r
8772 }\r
8773 \r
8774 int\r
8775 OpenLoopback(ProcRef *pr)\r
8776 {\r
8777   DisplayFatalError("Not implemented", 0, 1);\r
8778   return NO_ERROR;\r
8779 }\r
8780 \r
8781 \r
8782 int\r
8783 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8784 {\r
8785   ChildProc *cp;\r
8786   int err;\r
8787   SOCKET s, s2, s3;\r
8788   struct sockaddr_in sa, mysa;\r
8789   struct hostent FAR *hp;\r
8790   unsigned short uport;\r
8791   WORD wVersionRequested;\r
8792   WSADATA wsaData;\r
8793   int fromPort;\r
8794   char stderrPortStr[MSG_SIZ];\r
8795 \r
8796   /* Initialize socket DLL */\r
8797   wVersionRequested = MAKEWORD(1, 1);\r
8798   err = WSAStartup(wVersionRequested, &wsaData);\r
8799   if (err != 0) return err;\r
8800 \r
8801   /* Resolve remote host name */\r
8802   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8803   if (!(hp = gethostbyname(host))) {\r
8804     unsigned int b0, b1, b2, b3;\r
8805 \r
8806     err = WSAGetLastError();\r
8807 \r
8808     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8809       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8810       hp->h_addrtype = AF_INET;\r
8811       hp->h_length = 4;\r
8812       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8813       hp->h_addr_list[0] = (char *) malloc(4);\r
8814       hp->h_addr_list[0][0] = (char) b0;\r
8815       hp->h_addr_list[0][1] = (char) b1;\r
8816       hp->h_addr_list[0][2] = (char) b2;\r
8817       hp->h_addr_list[0][3] = (char) b3;\r
8818     } else {\r
8819       WSACleanup();\r
8820       return err;\r
8821     }\r
8822   }\r
8823   sa.sin_family = hp->h_addrtype;\r
8824   uport = (unsigned short) 514;\r
8825   sa.sin_port = htons(uport);\r
8826   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8827 \r
8828   /* Bind local socket to unused "privileged" port address\r
8829    */\r
8830   s = INVALID_SOCKET;\r
8831   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8832   mysa.sin_family = AF_INET;\r
8833   mysa.sin_addr.s_addr = INADDR_ANY;\r
8834   for (fromPort = 1023;; fromPort--) {\r
8835     if (fromPort < 0) {\r
8836       WSACleanup();\r
8837       return WSAEADDRINUSE;\r
8838     }\r
8839     if (s == INVALID_SOCKET) {\r
8840       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8841         err = WSAGetLastError();\r
8842         WSACleanup();\r
8843         return err;\r
8844       }\r
8845     }\r
8846     uport = (unsigned short) fromPort;\r
8847     mysa.sin_port = htons(uport);\r
8848     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8849         == SOCKET_ERROR) {\r
8850       err = WSAGetLastError();\r
8851       if (err == WSAEADDRINUSE) continue;\r
8852       WSACleanup();\r
8853       return err;\r
8854     }\r
8855     if (connect(s, (struct sockaddr *) &sa,\r
8856       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8857       err = WSAGetLastError();\r
8858       if (err == WSAEADDRINUSE) {\r
8859         closesocket(s);\r
8860         s = -1;\r
8861         continue;\r
8862       }\r
8863       WSACleanup();\r
8864       return err;\r
8865     }\r
8866     break;\r
8867   }\r
8868 \r
8869   /* Bind stderr local socket to unused "privileged" port address\r
8870    */\r
8871   s2 = INVALID_SOCKET;\r
8872   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8873   mysa.sin_family = AF_INET;\r
8874   mysa.sin_addr.s_addr = INADDR_ANY;\r
8875   for (fromPort = 1023;; fromPort--) {\r
8876     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8877     if (fromPort < 0) {\r
8878       (void) closesocket(s);\r
8879       WSACleanup();\r
8880       return WSAEADDRINUSE;\r
8881     }\r
8882     if (s2 == INVALID_SOCKET) {\r
8883       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8884         err = WSAGetLastError();\r
8885         closesocket(s);\r
8886         WSACleanup();\r
8887         return err;\r
8888       }\r
8889     }\r
8890     uport = (unsigned short) fromPort;\r
8891     mysa.sin_port = htons(uport);\r
8892     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8893         == SOCKET_ERROR) {\r
8894       err = WSAGetLastError();\r
8895       if (err == WSAEADDRINUSE) continue;\r
8896       (void) closesocket(s);\r
8897       WSACleanup();\r
8898       return err;\r
8899     }\r
8900     if (listen(s2, 1) == SOCKET_ERROR) {\r
8901       err = WSAGetLastError();\r
8902       if (err == WSAEADDRINUSE) {\r
8903         closesocket(s2);\r
8904         s2 = INVALID_SOCKET;\r
8905         continue;\r
8906       }\r
8907       (void) closesocket(s);\r
8908       (void) closesocket(s2);\r
8909       WSACleanup();\r
8910       return err;\r
8911     }\r
8912     break;\r
8913   }\r
8914   prevStderrPort = fromPort; // remember port used\r
8915   sprintf(stderrPortStr, "%d", fromPort);\r
8916 \r
8917   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
8918     err = WSAGetLastError();\r
8919     (void) closesocket(s);\r
8920     (void) closesocket(s2);\r
8921     WSACleanup();\r
8922     return err;\r
8923   }\r
8924 \r
8925   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
8926     err = WSAGetLastError();\r
8927     (void) closesocket(s);\r
8928     (void) closesocket(s2);\r
8929     WSACleanup();\r
8930     return err;\r
8931   }\r
8932   if (*user == NULLCHAR) user = UserName();\r
8933   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
8934     err = WSAGetLastError();\r
8935     (void) closesocket(s);\r
8936     (void) closesocket(s2);\r
8937     WSACleanup();\r
8938     return err;\r
8939   }\r
8940   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
8941     err = WSAGetLastError();\r
8942     (void) closesocket(s);\r
8943     (void) closesocket(s2);\r
8944     WSACleanup();\r
8945     return err;\r
8946   }\r
8947 \r
8948   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
8949     err = WSAGetLastError();\r
8950     (void) closesocket(s);\r
8951     (void) closesocket(s2);\r
8952     WSACleanup();\r
8953     return err;\r
8954   }\r
8955   (void) closesocket(s2);  /* Stop listening */\r
8956 \r
8957   /* Prepare return value */\r
8958   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8959   cp->kind = CPRcmd;\r
8960   cp->sock = s;\r
8961   cp->sock2 = s3;\r
8962   *pr = (ProcRef *) cp;\r
8963 \r
8964   return NO_ERROR;\r
8965 }\r
8966 \r
8967 \r
8968 InputSourceRef\r
8969 AddInputSource(ProcRef pr, int lineByLine,\r
8970                InputCallback func, VOIDSTAR closure)\r
8971 {\r
8972   InputSource *is, *is2 = NULL;\r
8973   ChildProc *cp = (ChildProc *) pr;\r
8974 \r
8975   is = (InputSource *) calloc(1, sizeof(InputSource));\r
8976   is->lineByLine = lineByLine;\r
8977   is->func = func;\r
8978   is->closure = closure;\r
8979   is->second = NULL;\r
8980   is->next = is->buf;\r
8981   if (pr == NoProc) {\r
8982     is->kind = CPReal;\r
8983     consoleInputSource = is;\r
8984   } else {\r
8985     is->kind = cp->kind;\r
8986     /* \r
8987         [AS] Try to avoid a race condition if the thread is given control too early:\r
8988         we create all threads suspended so that the is->hThread variable can be\r
8989         safely assigned, then let the threads start with ResumeThread.\r
8990     */\r
8991     switch (cp->kind) {\r
8992     case CPReal:\r
8993       is->hFile = cp->hFrom;\r
8994       cp->hFrom = NULL; /* now owned by InputThread */\r
8995       is->hThread =\r
8996         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
8997                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
8998       break;\r
8999 \r
9000     case CPComm:\r
9001       is->hFile = cp->hFrom;\r
9002       cp->hFrom = NULL; /* now owned by InputThread */\r
9003       is->hThread =\r
9004         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9005                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9006       break;\r
9007 \r
9008     case CPSock:\r
9009       is->sock = cp->sock;\r
9010       is->hThread =\r
9011         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9012                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9013       break;\r
9014 \r
9015     case CPRcmd:\r
9016       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9017       *is2 = *is;\r
9018       is->sock = cp->sock;\r
9019       is->second = is2;\r
9020       is2->sock = cp->sock2;\r
9021       is2->second = is2;\r
9022       is->hThread =\r
9023         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9024                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9025       is2->hThread =\r
9026         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9027                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9028       break;\r
9029     }\r
9030 \r
9031     if( is->hThread != NULL ) {\r
9032         ResumeThread( is->hThread );\r
9033     }\r
9034 \r
9035     if( is2 != NULL && is2->hThread != NULL ) {\r
9036         ResumeThread( is2->hThread );\r
9037     }\r
9038   }\r
9039 \r
9040   return (InputSourceRef) is;\r
9041 }\r
9042 \r
9043 void\r
9044 RemoveInputSource(InputSourceRef isr)\r
9045 {\r
9046   InputSource *is;\r
9047 \r
9048   is = (InputSource *) isr;\r
9049   is->hThread = NULL;  /* tell thread to stop */\r
9050   CloseHandle(is->hThread);\r
9051   if (is->second != NULL) {\r
9052     is->second->hThread = NULL;\r
9053     CloseHandle(is->second->hThread);\r
9054   }\r
9055 }\r
9056 \r
9057 int no_wrap(char *message, int count)\r
9058 {\r
9059     ConsoleOutput(message, count, FALSE);\r
9060     return count;\r
9061 }\r
9062 \r
9063 int\r
9064 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9065 {\r
9066   DWORD dOutCount;\r
9067   int outCount = SOCKET_ERROR;\r
9068   ChildProc *cp = (ChildProc *) pr;\r
9069   static OVERLAPPED ovl;\r
9070   static int line = 0;\r
9071 \r
9072   if (pr == NoProc)\r
9073   {\r
9074     if (appData.noJoin || !appData.useInternalWrap)\r
9075       return no_wrap(message, count);\r
9076     else\r
9077     {\r
9078       int width = get_term_width();\r
9079       int len = wrap(NULL, message, count, width, &line);\r
9080       char *msg = malloc(len);\r
9081       int dbgchk;\r
9082 \r
9083       if (!msg)\r
9084         return no_wrap(message, count);\r
9085       else\r
9086       {\r
9087         dbgchk = wrap(msg, message, count, width, &line);\r
9088         if (dbgchk != len && appData.debugMode)\r
9089             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9090         ConsoleOutput(msg, len, FALSE);\r
9091         free(msg);\r
9092         return len;\r
9093       }\r
9094     }\r
9095   }\r
9096 \r
9097   if (ovl.hEvent == NULL) {\r
9098     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9099   }\r
9100   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9101 \r
9102   switch (cp->kind) {\r
9103   case CPSock:\r
9104   case CPRcmd:\r
9105     outCount = send(cp->sock, message, count, 0);\r
9106     if (outCount == SOCKET_ERROR) {\r
9107       *outError = WSAGetLastError();\r
9108     } else {\r
9109       *outError = NO_ERROR;\r
9110     }\r
9111     break;\r
9112 \r
9113   case CPReal:\r
9114     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9115                   &dOutCount, NULL)) {\r
9116       *outError = NO_ERROR;\r
9117       outCount = (int) dOutCount;\r
9118     } else {\r
9119       *outError = GetLastError();\r
9120     }\r
9121     break;\r
9122 \r
9123   case CPComm:\r
9124     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9125                             &dOutCount, &ovl);\r
9126     if (*outError == NO_ERROR) {\r
9127       outCount = (int) dOutCount;\r
9128     }\r
9129     break;\r
9130   }\r
9131   return outCount;\r
9132 }\r
9133 \r
9134 int\r
9135 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9136                        long msdelay)\r
9137 {\r
9138   /* Ignore delay, not implemented for WinBoard */\r
9139   return OutputToProcess(pr, message, count, outError);\r
9140 }\r
9141 \r
9142 \r
9143 void\r
9144 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9145                         char *buf, int count, int error)\r
9146 {\r
9147   DisplayFatalError("Not implemented", 0, 1);\r
9148 }\r
9149 \r
9150 /* see wgamelist.c for Game List functions */\r
9151 /* see wedittags.c for Edit Tags functions */\r
9152 \r
9153 \r
9154 VOID\r
9155 ICSInitScript()\r
9156 {\r
9157   FILE *f;\r
9158   char buf[MSG_SIZ];\r
9159   char *dummy;\r
9160 \r
9161   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9162     f = fopen(buf, "r");\r
9163     if (f != NULL) {\r
9164       ProcessICSInitScript(f);\r
9165       fclose(f);\r
9166     }\r
9167   }\r
9168 }\r
9169 \r
9170 \r
9171 VOID\r
9172 StartAnalysisClock()\r
9173 {\r
9174   if (analysisTimerEvent) return;\r
9175   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9176                                         (UINT) 2000, NULL);\r
9177 }\r
9178 \r
9179 VOID\r
9180 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9181 {\r
9182   highlightInfo.sq[0].x = fromX;\r
9183   highlightInfo.sq[0].y = fromY;\r
9184   highlightInfo.sq[1].x = toX;\r
9185   highlightInfo.sq[1].y = toY;\r
9186 }\r
9187 \r
9188 VOID\r
9189 ClearHighlights()\r
9190 {\r
9191   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9192     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9193 }\r
9194 \r
9195 VOID\r
9196 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9197 {\r
9198   premoveHighlightInfo.sq[0].x = fromX;\r
9199   premoveHighlightInfo.sq[0].y = fromY;\r
9200   premoveHighlightInfo.sq[1].x = toX;\r
9201   premoveHighlightInfo.sq[1].y = toY;\r
9202 }\r
9203 \r
9204 VOID\r
9205 ClearPremoveHighlights()\r
9206 {\r
9207   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9208     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9209 }\r
9210 \r
9211 VOID\r
9212 ShutDownFrontEnd()\r
9213 {\r
9214   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9215   DeleteClipboardTempFiles();\r
9216 }\r
9217 \r
9218 void\r
9219 BoardToTop()\r
9220 {\r
9221     if (IsIconic(hwndMain))\r
9222       ShowWindow(hwndMain, SW_RESTORE);\r
9223 \r
9224     SetActiveWindow(hwndMain);\r
9225 }\r
9226 \r
9227 /*\r
9228  * Prototypes for animation support routines\r
9229  */\r
9230 static void ScreenSquare(int column, int row, POINT * pt);\r
9231 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9232      POINT frames[], int * nFrames);\r
9233 \r
9234 \r
9235 void\r
9236 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9237 {       // [HGM] atomic: animate blast wave\r
9238         int i;\r
9239 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9240         explodeInfo.fromX = fromX;\r
9241         explodeInfo.fromY = fromY;\r
9242         explodeInfo.toX = toX;\r
9243         explodeInfo.toY = toY;\r
9244         for(i=1; i<nFrames; i++) {\r
9245             explodeInfo.radius = (i*180)/(nFrames-1);\r
9246             DrawPosition(FALSE, NULL);\r
9247             Sleep(appData.animSpeed);\r
9248         }\r
9249         explodeInfo.radius = 0;\r
9250         DrawPosition(TRUE, NULL);\r
9251 }\r
9252 \r
9253 #define kFactor 4\r
9254 \r
9255 void\r
9256 AnimateMove(board, fromX, fromY, toX, toY)\r
9257      Board board;\r
9258      int fromX;\r
9259      int fromY;\r
9260      int toX;\r
9261      int toY;\r
9262 {\r
9263   ChessSquare piece;\r
9264   POINT start, finish, mid;\r
9265   POINT frames[kFactor * 2 + 1];\r
9266   int nFrames, n;\r
9267 \r
9268   if (!appData.animate) return;\r
9269   if (doingSizing) return;\r
9270   if (fromY < 0 || fromX < 0) return;\r
9271   piece = board[fromY][fromX];\r
9272   if (piece >= EmptySquare) return;\r
9273 \r
9274   ScreenSquare(fromX, fromY, &start);\r
9275   ScreenSquare(toX, toY, &finish);\r
9276 \r
9277   /* All pieces except knights move in straight line */\r
9278   if (piece != WhiteKnight && piece != BlackKnight) {\r
9279     mid.x = start.x + (finish.x - start.x) / 2;\r
9280     mid.y = start.y + (finish.y - start.y) / 2;\r
9281   } else {\r
9282     /* Knight: make diagonal movement then straight */\r
9283     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9284        mid.x = start.x + (finish.x - start.x) / 2;\r
9285        mid.y = finish.y;\r
9286      } else {\r
9287        mid.x = finish.x;\r
9288        mid.y = start.y + (finish.y - start.y) / 2;\r
9289      }\r
9290   }\r
9291   \r
9292   /* Don't use as many frames for very short moves */\r
9293   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9294     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9295   else\r
9296     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9297 \r
9298   animInfo.from.x = fromX;\r
9299   animInfo.from.y = fromY;\r
9300   animInfo.to.x = toX;\r
9301   animInfo.to.y = toY;\r
9302   animInfo.lastpos = start;\r
9303   animInfo.piece = piece;\r
9304   for (n = 0; n < nFrames; n++) {\r
9305     animInfo.pos = frames[n];\r
9306     DrawPosition(FALSE, NULL);\r
9307     animInfo.lastpos = animInfo.pos;\r
9308     Sleep(appData.animSpeed);\r
9309   }\r
9310   animInfo.pos = finish;\r
9311   DrawPosition(FALSE, NULL);\r
9312   animInfo.piece = EmptySquare;\r
9313   if(gameInfo.variant == VariantAtomic && \r
9314      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9315         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9316 }\r
9317 \r
9318 /*      Convert board position to corner of screen rect and color       */\r
9319 \r
9320 static void\r
9321 ScreenSquare(column, row, pt)\r
9322      int column; int row; POINT * pt;\r
9323 {\r
9324   if (flipView) {\r
9325     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9326     pt->y = lineGap + row * (squareSize + lineGap);\r
9327   } else {\r
9328     pt->x = lineGap + column * (squareSize + lineGap);\r
9329     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9330   }\r
9331 }\r
9332 \r
9333 /*      Generate a series of frame coords from start->mid->finish.\r
9334         The movement rate doubles until the half way point is\r
9335         reached, then halves back down to the final destination,\r
9336         which gives a nice slow in/out effect. The algorithmn\r
9337         may seem to generate too many intermediates for short\r
9338         moves, but remember that the purpose is to attract the\r
9339         viewers attention to the piece about to be moved and\r
9340         then to where it ends up. Too few frames would be less\r
9341         noticeable.                                             */\r
9342 \r
9343 static void\r
9344 Tween(start, mid, finish, factor, frames, nFrames)\r
9345      POINT * start; POINT * mid;\r
9346      POINT * finish; int factor;\r
9347      POINT frames[]; int * nFrames;\r
9348 {\r
9349   int n, fraction = 1, count = 0;\r
9350 \r
9351   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9352   for (n = 0; n < factor; n++)\r
9353     fraction *= 2;\r
9354   for (n = 0; n < factor; n++) {\r
9355     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9356     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9357     count ++;\r
9358     fraction = fraction / 2;\r
9359   }\r
9360   \r
9361   /* Midpoint */\r
9362   frames[count] = *mid;\r
9363   count ++;\r
9364   \r
9365   /* Slow out, stepping 1/2, then 1/4, ... */\r
9366   fraction = 2;\r
9367   for (n = 0; n < factor; n++) {\r
9368     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9369     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9370     count ++;\r
9371     fraction = fraction * 2;\r
9372   }\r
9373   *nFrames = count;\r
9374 }\r
9375 \r
9376 void\r
9377 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9378 {\r
9379     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9380 \r
9381     EvalGraphSet( first, last, current, pvInfoList );\r
9382 }\r