Convert to DOS line endings
[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);\r
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:\r
3761   case WM_RBUTTONUP:\r
3762     ReleaseCapture();\r
3763     UnLoadPV();\r
3764     break;\r
3765  \r
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 IcsExamining:\r
3790       if(x < BOARD_LEFT || x >= BOARD_RGHT) break;\r
3791     case EditPosition:\r
3792       if (x == BOARD_LEFT-1 || x == BOARD_RGHT) break;\r
3793       if (x < 0 || y < 0) break;\r
3794       fromX = x;\r
3795       fromY = y;\r
3796       if (message == WM_MBUTTONDOWN) {\r
3797         buttonCount = 3;  /* even if system didn't think so */\r
3798         if (wParam & MK_SHIFT) \r
3799           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3800         else\r
3801           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3802       } else { /* message == WM_RBUTTONDOWN */\r
3803         /* Just have one menu, on the right button.  Windows users don't\r
3804            think to try the middle one, and sometimes other software steals\r
3805            it, or it doesn't really exist. */\r
3806         if(gameInfo.variant != VariantShogi)\r
3807             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3808         else\r
3809             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
3810       }\r
3811       break;\r
3812     case IcsObserving:\r
3813       if(!appData.icsEngineAnalyze) break;\r
3814     case IcsPlayingWhite:\r
3815     case IcsPlayingBlack:\r
3816       if(!appData.zippyPlay) goto noZip;\r
3817     case MachinePlaysWhite:\r
3818     case MachinePlaysBlack:\r
3819     case TwoMachinesPlay:\r
3820     case AnalyzeMode:\r
3821     case AnalyzeFile:\r
3822       if (!appData.dropMenu) {\r
3823         SetCapture(hwndMain);\r
3824         LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);\r
3825         break;\r
3826       }\r
3827       if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||\r
3828          gameMode == AnalyzeFile || gameMode == IcsObserving) break;\r
3829     case EditGame:\r
3830     noZip:\r
3831       if (x < 0 || y < 0) break;\r
3832       if (!appData.dropMenu || appData.testLegality &&\r
3833           gameInfo.variant != VariantBughouse &&\r
3834           gameInfo.variant != VariantCrazyhouse) break;\r
3835       fromX = x;\r
3836       fromY = y;\r
3837       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3838       SetupDropMenu(hmenu);\r
3839       MenuPopup(hwnd, pt, hmenu, -1);\r
3840       break;\r
3841     default:\r
3842       break;\r
3843     }\r
3844     break;\r
3845   }\r
3846 \r
3847   recursive--;\r
3848 }\r
3849 \r
3850 /* Preprocess messages for buttons in main window */\r
3851 LRESULT CALLBACK\r
3852 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3853 {\r
3854   int id = GetWindowLong(hwnd, GWL_ID);\r
3855   int i, dir;\r
3856 \r
3857   for (i=0; i<N_BUTTONS; i++) {\r
3858     if (buttonDesc[i].id == id) break;\r
3859   }\r
3860   if (i == N_BUTTONS) return 0;\r
3861   switch (message) {\r
3862   case WM_KEYDOWN:\r
3863     switch (wParam) {\r
3864     case VK_LEFT:\r
3865     case VK_RIGHT:\r
3866       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3867       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3868       return TRUE;\r
3869     }\r
3870     break;\r
3871   case WM_CHAR:\r
3872     switch (wParam) {\r
3873     case '\r':\r
3874       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3875       return TRUE;\r
3876     default:\r
3877       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
3878         // [HGM] movenum: only letters or leading zero should go to ICS input\r
3879         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3880         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3881         SetFocus(h);\r
3882         SendMessage(h, WM_CHAR, wParam, lParam);\r
3883         return TRUE;\r
3884       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3885         PopUpMoveDialog((char)wParam);\r
3886       }\r
3887       break;\r
3888     }\r
3889     break;\r
3890   }\r
3891   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3892 }\r
3893 \r
3894 /* Process messages for Promotion dialog box */\r
3895 LRESULT CALLBACK\r
3896 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3897 {\r
3898   char promoChar;\r
3899 \r
3900   switch (message) {\r
3901   case WM_INITDIALOG: /* message: initialize dialog box */\r
3902     /* Center the dialog over the application window */\r
3903     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
3904     ShowWindow(GetDlgItem(hDlg, PB_King), \r
3905       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
3906        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
3907                SW_SHOW : SW_HIDE);\r
3908     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
3909     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
3910        ((PieceToChar(WhiteAngel) >= 'A' &&\r
3911          PieceToChar(WhiteAngel) != '~') ||\r
3912         (PieceToChar(BlackAngel) >= 'A' &&\r
3913          PieceToChar(BlackAngel) != '~')   ) ?\r
3914                SW_SHOW : SW_HIDE);\r
3915     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
3916        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
3917          PieceToChar(WhiteMarshall) != '~') ||\r
3918         (PieceToChar(BlackMarshall) >= 'A' &&\r
3919          PieceToChar(BlackMarshall) != '~')   ) ?\r
3920                SW_SHOW : SW_HIDE);\r
3921     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
3922     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
3923        gameInfo.variant != VariantShogi ?\r
3924                SW_SHOW : SW_HIDE);\r
3925     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
3926        gameInfo.variant != VariantShogi ?\r
3927                SW_SHOW : SW_HIDE);\r
3928     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
3929        gameInfo.variant == VariantShogi ?\r
3930                SW_SHOW : SW_HIDE);\r
3931     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
3932        gameInfo.variant == VariantShogi ?\r
3933                SW_SHOW : SW_HIDE);\r
3934     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
3935        gameInfo.variant == VariantSuper ?\r
3936                SW_SHOW : SW_HIDE);\r
3937     return TRUE;\r
3938 \r
3939   case WM_COMMAND: /* message: received a command */\r
3940     switch (LOWORD(wParam)) {\r
3941     case IDCANCEL:\r
3942       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3943       ClearHighlights();\r
3944       DrawPosition(FALSE, NULL);\r
3945       return TRUE;\r
3946     case PB_King:\r
3947       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
3948       break;\r
3949     case PB_Queen:\r
3950       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
3951       break;\r
3952     case PB_Rook:\r
3953       promoChar = PieceToChar(BlackRook);\r
3954       break;\r
3955     case PB_Bishop:\r
3956       promoChar = PieceToChar(BlackBishop);\r
3957       break;\r
3958     case PB_Chancellor:\r
3959       promoChar = PieceToChar(BlackMarshall);\r
3960       break;\r
3961     case PB_Archbishop:\r
3962       promoChar = PieceToChar(BlackAngel);\r
3963       break;\r
3964     case PB_Knight:\r
3965       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
3966       break;\r
3967     default:\r
3968       return FALSE;\r
3969     }\r
3970     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3971     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
3972        only show the popup when we are already sure the move is valid or\r
3973        legal. We pass a faulty move type, but the kludge is that FinishMove\r
3974        will figure out it is a promotion from the promoChar. */\r
3975     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3976     fromX = fromY = -1;\r
3977     if (!appData.highlightLastMove) {\r
3978       ClearHighlights();\r
3979       DrawPosition(FALSE, NULL);\r
3980     }\r
3981     return TRUE;\r
3982   }\r
3983   return FALSE;\r
3984 }\r
3985 \r
3986 /* Pop up promotion dialog */\r
3987 VOID\r
3988 PromotionPopup(HWND hwnd)\r
3989 {\r
3990   FARPROC lpProc;\r
3991 \r
3992   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3993   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3994     hwnd, (DLGPROC)lpProc);\r
3995   FreeProcInstance(lpProc);\r
3996 }\r
3997 \r
3998 void\r
3999 PromotionPopUp()\r
4000 {\r
4001   DrawPosition(TRUE, NULL);\r
4002   PromotionPopup(hwndMain);\r
4003 }\r
4004 \r
4005 /* Toggle ShowThinking */\r
4006 VOID\r
4007 ToggleShowThinking()\r
4008 {\r
4009   appData.showThinking = !appData.showThinking;\r
4010   ShowThinkingEvent();\r
4011 }\r
4012 \r
4013 VOID\r
4014 LoadGameDialog(HWND hwnd, char* title)\r
4015 {\r
4016   UINT number = 0;\r
4017   FILE *f;\r
4018   char fileTitle[MSG_SIZ];\r
4019   f = OpenFileDialog(hwnd, "rb", "",\r
4020                      appData.oldSaveStyle ? "gam" : "pgn",\r
4021                      GAME_FILT,\r
4022                      title, &number, fileTitle, NULL);\r
4023   if (f != NULL) {\r
4024     cmailMsgLoaded = FALSE;\r
4025     if (number == 0) {\r
4026       int error = GameListBuild(f);\r
4027       if (error) {\r
4028         DisplayError("Cannot build game list", error);\r
4029       } else if (!ListEmpty(&gameList) &&\r
4030                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4031         GameListPopUp(f, fileTitle);\r
4032         return;\r
4033       }\r
4034       GameListDestroy();\r
4035       number = 1;\r
4036     }\r
4037     LoadGame(f, number, fileTitle, FALSE);\r
4038   }\r
4039 }\r
4040 \r
4041 int get_term_width()\r
4042 {\r
4043     HDC hdc;\r
4044     TEXTMETRIC tm;\r
4045     RECT rc;\r
4046     HFONT hfont, hold_font;\r
4047     LOGFONT lf;\r
4048     HWND hText;\r
4049 \r
4050     if (hwndConsole)\r
4051         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4052     else\r
4053         return 79;\r
4054 \r
4055     // get the text metrics\r
4056     hdc = GetDC(hText);\r
4057     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4058     if (consoleCF.dwEffects & CFE_BOLD)\r
4059         lf.lfWeight = FW_BOLD;\r
4060     if (consoleCF.dwEffects & CFE_ITALIC)\r
4061         lf.lfItalic = TRUE;\r
4062     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4063         lf.lfStrikeOut = TRUE;\r
4064     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4065         lf.lfUnderline = TRUE;\r
4066     hfont = CreateFontIndirect(&lf);\r
4067     hold_font = SelectObject(hdc, hfont);\r
4068     GetTextMetrics(hdc, &tm);\r
4069     SelectObject(hdc, hold_font);\r
4070     DeleteObject(hfont);\r
4071     ReleaseDC(hText, hdc);\r
4072 \r
4073     // get the rectangle\r
4074     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4075 \r
4076     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4077 }\r
4078 \r
4079 void UpdateICSWidth(HWND hText)\r
4080 {\r
4081     LONG old_width, new_width;\r
4082 \r
4083     new_width = get_term_width(hText, FALSE);\r
4084     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4085     if (new_width != old_width)\r
4086     {\r
4087         ics_update_width(new_width);\r
4088         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4089     }\r
4090 }\r
4091 \r
4092 VOID\r
4093 ChangedConsoleFont()\r
4094 {\r
4095   CHARFORMAT cfmt;\r
4096   CHARRANGE tmpsel, sel;\r
4097   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4098   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4099   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4100   PARAFORMAT paraf;\r
4101 \r
4102   cfmt.cbSize = sizeof(CHARFORMAT);\r
4103   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4104   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4105   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4106    * size.  This was undocumented in the version of MSVC++ that I had\r
4107    * when I wrote the code, but is apparently documented now.\r
4108    */\r
4109   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4110   cfmt.bCharSet = f->lf.lfCharSet;\r
4111   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4112   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4113   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4114   /* Why are the following seemingly needed too? */\r
4115   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4116   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4117   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4118   tmpsel.cpMin = 0;\r
4119   tmpsel.cpMax = -1; /*999999?*/\r
4120   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4121   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4122   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4123    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4124    */\r
4125   paraf.cbSize = sizeof(paraf);\r
4126   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4127   paraf.dxStartIndent = 0;\r
4128   paraf.dxOffset = WRAP_INDENT;\r
4129   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4130   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4131   UpdateICSWidth(hText);\r
4132 }\r
4133 \r
4134 /*---------------------------------------------------------------------------*\\r
4135  *\r
4136  * Window Proc for main window\r
4137  *\r
4138 \*---------------------------------------------------------------------------*/\r
4139 \r
4140 /* Process messages for main window, etc. */\r
4141 LRESULT CALLBACK\r
4142 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4143 {\r
4144   FARPROC lpProc;\r
4145   int wmId, wmEvent;\r
4146   char *defName;\r
4147   FILE *f;\r
4148   UINT number;\r
4149   char fileTitle[MSG_SIZ];\r
4150   char buf[MSG_SIZ];\r
4151   static SnapData sd;\r
4152 \r
4153   switch (message) {\r
4154 \r
4155   case WM_PAINT: /* message: repaint portion of window */\r
4156     PaintProc(hwnd);\r
4157     break;\r
4158 \r
4159   case WM_ERASEBKGND:\r
4160     if (IsIconic(hwnd)) {\r
4161       /* Cheat; change the message */\r
4162       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4163     } else {\r
4164       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4165     }\r
4166     break;\r
4167 \r
4168   case WM_LBUTTONDOWN:\r
4169   case WM_MBUTTONDOWN:\r
4170   case WM_RBUTTONDOWN:\r
4171   case WM_LBUTTONUP:\r
4172   case WM_MBUTTONUP:\r
4173   case WM_RBUTTONUP:\r
4174   case WM_MOUSEMOVE:\r
4175   case WM_MOUSEWHEEL:\r
4176     MouseEvent(hwnd, message, wParam, lParam);\r
4177     break;\r
4178 \r
4179   JAWS_KB_NAVIGATION\r
4180 \r
4181   case WM_CHAR:\r
4182     \r
4183     JAWS_ALT_INTERCEPT\r
4184 \r
4185     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4186         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4187         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4188         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4189         SetFocus(h);\r
4190         SendMessage(h, message, wParam, lParam);\r
4191     } else if(lParam != KF_REPEAT) {\r
4192         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4193                 PopUpMoveDialog((char)wParam);\r
4194         } else if((char)wParam == 003) CopyGameToClipboard();\r
4195          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4196     }\r
4197 \r
4198     break;\r
4199 \r
4200   case WM_PALETTECHANGED:\r
4201     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4202       int nnew;\r
4203       HDC hdc = GetDC(hwndMain);\r
4204       SelectPalette(hdc, hPal, TRUE);\r
4205       nnew = RealizePalette(hdc);\r
4206       if (nnew > 0) {\r
4207         paletteChanged = TRUE;\r
4208         InvalidateRect(hwnd, &boardRect, FALSE);\r
4209       }\r
4210       ReleaseDC(hwnd, hdc);\r
4211     }\r
4212     break;\r
4213 \r
4214   case WM_QUERYNEWPALETTE:\r
4215     if (!appData.monoMode /*&& paletteChanged*/) {\r
4216       int nnew;\r
4217       HDC hdc = GetDC(hwndMain);\r
4218       paletteChanged = FALSE;\r
4219       SelectPalette(hdc, hPal, FALSE);\r
4220       nnew = RealizePalette(hdc);\r
4221       if (nnew > 0) {\r
4222         InvalidateRect(hwnd, &boardRect, FALSE);\r
4223       }\r
4224       ReleaseDC(hwnd, hdc);\r
4225       return TRUE;\r
4226     }\r
4227     return FALSE;\r
4228 \r
4229   case WM_COMMAND: /* message: command from application menu */\r
4230     wmId    = LOWORD(wParam);\r
4231     wmEvent = HIWORD(wParam);\r
4232 \r
4233     switch (wmId) {\r
4234     case IDM_NewGame:\r
4235       ResetGameEvent();\r
4236       SAY("new game enter a move to play against the computer with white");\r
4237       break;\r
4238 \r
4239     case IDM_NewGameFRC:\r
4240       if( NewGameFRC() == 0 ) {\r
4241         ResetGameEvent();\r
4242       }\r
4243       break;\r
4244 \r
4245     case IDM_NewVariant:\r
4246       NewVariantPopup(hwnd);\r
4247       break;\r
4248 \r
4249     case IDM_LoadGame:\r
4250       LoadGameDialog(hwnd, "Load Game from File");\r
4251       break;\r
4252 \r
4253     case IDM_LoadNextGame:\r
4254       ReloadGame(1);\r
4255       break;\r
4256 \r
4257     case IDM_LoadPrevGame:\r
4258       ReloadGame(-1);\r
4259       break;\r
4260 \r
4261     case IDM_ReloadGame:\r
4262       ReloadGame(0);\r
4263       break;\r
4264 \r
4265     case IDM_LoadPosition:\r
4266       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4267         Reset(FALSE, TRUE);\r
4268       }\r
4269       number = 1;\r
4270       f = OpenFileDialog(hwnd, "rb", "",\r
4271                          appData.oldSaveStyle ? "pos" : "fen",\r
4272                          POSITION_FILT,\r
4273                          "Load Position from File", &number, fileTitle, NULL);\r
4274       if (f != NULL) {\r
4275         LoadPosition(f, number, fileTitle);\r
4276       }\r
4277       break;\r
4278 \r
4279     case IDM_LoadNextPosition:\r
4280       ReloadPosition(1);\r
4281       break;\r
4282 \r
4283     case IDM_LoadPrevPosition:\r
4284       ReloadPosition(-1);\r
4285       break;\r
4286 \r
4287     case IDM_ReloadPosition:\r
4288       ReloadPosition(0);\r
4289       break;\r
4290 \r
4291     case IDM_SaveGame:\r
4292       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4293       f = OpenFileDialog(hwnd, "a", defName,\r
4294                          appData.oldSaveStyle ? "gam" : "pgn",\r
4295                          GAME_FILT,\r
4296                          "Save Game to File", NULL, fileTitle, NULL);\r
4297       if (f != NULL) {\r
4298         SaveGame(f, 0, "");\r
4299       }\r
4300       break;\r
4301 \r
4302     case IDM_SavePosition:\r
4303       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4304       f = OpenFileDialog(hwnd, "a", defName,\r
4305                          appData.oldSaveStyle ? "pos" : "fen",\r
4306                          POSITION_FILT,\r
4307                          "Save Position to File", NULL, fileTitle, NULL);\r
4308       if (f != NULL) {\r
4309         SavePosition(f, 0, "");\r
4310       }\r
4311       break;\r
4312 \r
4313     case IDM_SaveDiagram:\r
4314       defName = "diagram";\r
4315       f = OpenFileDialog(hwnd, "wb", defName,\r
4316                          "bmp",\r
4317                          DIAGRAM_FILT,\r
4318                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4319       if (f != NULL) {\r
4320         SaveDiagram(f);\r
4321       }\r
4322       break;\r
4323 \r
4324     case IDM_CopyGame:\r
4325       CopyGameToClipboard();\r
4326       break;\r
4327 \r
4328     case IDM_PasteGame:\r
4329       PasteGameFromClipboard();\r
4330       break;\r
4331 \r
4332     case IDM_CopyGameListToClipboard:\r
4333       CopyGameListToClipboard();\r
4334       break;\r
4335 \r
4336     /* [AS] Autodetect FEN or PGN data */\r
4337     case IDM_PasteAny:\r
4338       PasteGameOrFENFromClipboard();\r
4339       break;\r
4340 \r
4341     /* [AS] Move history */\r
4342     case IDM_ShowMoveHistory:\r
4343         if( MoveHistoryIsUp() ) {\r
4344             MoveHistoryPopDown();\r
4345         }\r
4346         else {\r
4347             MoveHistoryPopUp();\r
4348         }\r
4349         break;\r
4350 \r
4351     /* [AS] Eval graph */\r
4352     case IDM_ShowEvalGraph:\r
4353         if( EvalGraphIsUp() ) {\r
4354             EvalGraphPopDown();\r
4355         }\r
4356         else {\r
4357             EvalGraphPopUp();\r
4358             SetFocus(hwndMain);\r
4359         }\r
4360         break;\r
4361 \r
4362     /* [AS] Engine output */\r
4363     case IDM_ShowEngineOutput:\r
4364         if( EngineOutputIsUp() ) {\r
4365             EngineOutputPopDown();\r
4366         }\r
4367         else {\r
4368             EngineOutputPopUp();\r
4369         }\r
4370         break;\r
4371 \r
4372     /* [AS] User adjudication */\r
4373     case IDM_UserAdjudication_White:\r
4374         UserAdjudicationEvent( +1 );\r
4375         break;\r
4376 \r
4377     case IDM_UserAdjudication_Black:\r
4378         UserAdjudicationEvent( -1 );\r
4379         break;\r
4380 \r
4381     case IDM_UserAdjudication_Draw:\r
4382         UserAdjudicationEvent( 0 );\r
4383         break;\r
4384 \r
4385     /* [AS] Game list options dialog */\r
4386     case IDM_GameListOptions:\r
4387       GameListOptions();\r
4388       break;\r
4389 \r
4390     case IDM_NewChat:\r
4391       ChatPopUp();\r
4392       break;\r
4393 \r
4394     case IDM_CopyPosition:\r
4395       CopyFENToClipboard();\r
4396       break;\r
4397 \r
4398     case IDM_PastePosition:\r
4399       PasteFENFromClipboard();\r
4400       break;\r
4401 \r
4402     case IDM_MailMove:\r
4403       MailMoveEvent();\r
4404       break;\r
4405 \r
4406     case IDM_ReloadCMailMsg:\r
4407       Reset(TRUE, TRUE);\r
4408       ReloadCmailMsgEvent(FALSE);\r
4409       break;\r
4410 \r
4411     case IDM_Minimize:\r
4412       ShowWindow(hwnd, SW_MINIMIZE);\r
4413       break;\r
4414 \r
4415     case IDM_Exit:\r
4416       ExitEvent(0);\r
4417       break;\r
4418 \r
4419     case IDM_MachineWhite:\r
4420       MachineWhiteEvent();\r
4421       /*\r
4422        * refresh the tags dialog only if it's visible\r
4423        */\r
4424       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4425           char *tags;\r
4426           tags = PGNTags(&gameInfo);\r
4427           TagsPopUp(tags, CmailMsg());\r
4428           free(tags);\r
4429       }\r
4430       SAY("computer starts playing white");\r
4431       break;\r
4432 \r
4433     case IDM_MachineBlack:\r
4434       MachineBlackEvent();\r
4435       /*\r
4436        * refresh the tags dialog only if it's visible\r
4437        */\r
4438       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4439           char *tags;\r
4440           tags = PGNTags(&gameInfo);\r
4441           TagsPopUp(tags, CmailMsg());\r
4442           free(tags);\r
4443       }\r
4444       SAY("computer starts playing black");\r
4445       break;\r
4446 \r
4447     case IDM_TwoMachines:\r
4448       TwoMachinesEvent();\r
4449       /*\r
4450        * refresh the tags dialog only if it's visible\r
4451        */\r
4452       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4453           char *tags;\r
4454           tags = PGNTags(&gameInfo);\r
4455           TagsPopUp(tags, CmailMsg());\r
4456           free(tags);\r
4457       }\r
4458       SAY("programs start playing each other");\r
4459       break;\r
4460 \r
4461     case IDM_AnalysisMode:\r
4462       if (!first.analysisSupport) {\r
4463         sprintf(buf, "%s does not support analysis", first.tidy);\r
4464         DisplayError(buf, 0);\r
4465       } else {\r
4466         SAY("analyzing current position");\r
4467         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4468         if (appData.icsActive) {\r
4469                if (gameMode != IcsObserving) {\r
4470                        sprintf(buf, "You are not observing a game");\r
4471                        DisplayError(buf, 0);\r
4472                        /* secure check */\r
4473                        if (appData.icsEngineAnalyze) {\r
4474                                if (appData.debugMode) \r
4475                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4476                                ExitAnalyzeMode();\r
4477                                ModeHighlight();\r
4478                                break;\r
4479                        }\r
4480                        break;\r
4481                } else {\r
4482                        /* if enable, user want disable icsEngineAnalyze */\r
4483                        if (appData.icsEngineAnalyze) {\r
4484                                ExitAnalyzeMode();\r
4485                                ModeHighlight();\r
4486                                break;\r
4487                        }\r
4488                        appData.icsEngineAnalyze = TRUE;\r
4489                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4490                }\r
4491         } \r
4492         if (!appData.showThinking) ToggleShowThinking();\r
4493         AnalyzeModeEvent();\r
4494       }\r
4495       break;\r
4496 \r
4497     case IDM_AnalyzeFile:\r
4498       if (!first.analysisSupport) {\r
4499         char buf[MSG_SIZ];\r
4500         sprintf(buf, "%s does not support analysis", first.tidy);\r
4501         DisplayError(buf, 0);\r
4502       } else {\r
4503         if (!appData.showThinking) ToggleShowThinking();\r
4504         AnalyzeFileEvent();\r
4505         LoadGameDialog(hwnd, "Analyze Game from File");\r
4506         AnalysisPeriodicEvent(1);\r
4507       }\r
4508       break;\r
4509 \r
4510     case IDM_IcsClient:\r
4511       IcsClientEvent();\r
4512       break;\r
4513 \r
4514     case IDM_EditGame:\r
4515       EditGameEvent();\r
4516       SAY("edit game");\r
4517       break;\r
4518 \r
4519     case IDM_EditPosition:\r
4520       EditPositionEvent();\r
4521       SAY("to set up a position type a FEN");\r
4522       break;\r
4523 \r
4524     case IDM_Training:\r
4525       TrainingEvent();\r
4526       break;\r
4527 \r
4528     case IDM_ShowGameList:\r
4529       ShowGameListProc();\r
4530       break;\r
4531 \r
4532     case IDM_EditTags:\r
4533       EditTagsProc();\r
4534       break;\r
4535 \r
4536     case IDM_EditComment:\r
4537       if (commentUp && editComment) {\r
4538         CommentPopDown();\r
4539       } else {\r
4540         EditCommentEvent();\r
4541       }\r
4542       break;\r
4543 \r
4544     case IDM_Pause:\r
4545       PauseEvent();\r
4546       break;\r
4547 \r
4548     case IDM_Accept:\r
4549       AcceptEvent();\r
4550       break;\r
4551 \r
4552     case IDM_Decline:\r
4553       DeclineEvent();\r
4554       break;\r
4555 \r
4556     case IDM_Rematch:\r
4557       RematchEvent();\r
4558       break;\r
4559 \r
4560     case IDM_CallFlag:\r
4561       CallFlagEvent();\r
4562       break;\r
4563 \r
4564     case IDM_Draw:\r
4565       DrawEvent();\r
4566       break;\r
4567 \r
4568     case IDM_Adjourn:\r
4569       AdjournEvent();\r
4570       break;\r
4571 \r
4572     case IDM_Abort:\r
4573       AbortEvent();\r
4574       break;\r
4575 \r
4576     case IDM_Resign:\r
4577       ResignEvent();\r
4578       break;\r
4579 \r
4580     case IDM_StopObserving:\r
4581       StopObservingEvent();\r
4582       break;\r
4583 \r
4584     case IDM_StopExamining:\r
4585       StopExaminingEvent();\r
4586       break;\r
4587 \r
4588     case IDM_TypeInMove:\r
4589       PopUpMoveDialog('\000');\r
4590       break;\r
4591 \r
4592     case IDM_TypeInName:\r
4593       PopUpNameDialog('\000');\r
4594       break;\r
4595 \r
4596     case IDM_Backward:\r
4597       BackwardEvent();\r
4598       SetFocus(hwndMain);\r
4599       break;\r
4600 \r
4601     JAWS_MENU_ITEMS\r
4602 \r
4603     case IDM_Forward:\r
4604       ForwardEvent();\r
4605       SetFocus(hwndMain);\r
4606       break;\r
4607 \r
4608     case IDM_ToStart:\r
4609       ToStartEvent();\r
4610       SetFocus(hwndMain);\r
4611       break;\r
4612 \r
4613     case IDM_ToEnd:\r
4614       ToEndEvent();\r
4615       SetFocus(hwndMain);\r
4616       break;\r
4617 \r
4618     case IDM_Revert:\r
4619       RevertEvent();\r
4620       break;\r
4621 \r
4622     case IDM_TruncateGame:\r
4623       TruncateGameEvent();\r
4624       break;\r
4625 \r
4626     case IDM_MoveNow:\r
4627       MoveNowEvent();\r
4628       break;\r
4629 \r
4630     case IDM_RetractMove:\r
4631       RetractMoveEvent();\r
4632       break;\r
4633 \r
4634     case IDM_FlipView:\r
4635       flipView = !flipView;\r
4636       DrawPosition(FALSE, NULL);\r
4637       break;\r
4638 \r
4639     case IDM_FlipClock:\r
4640       flipClock = !flipClock;\r
4641       DisplayBothClocks();\r
4642       DrawPosition(FALSE, NULL);\r
4643       break;\r
4644 \r
4645     case IDM_MuteSounds:\r
4646       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4647       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4648                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4649       break;\r
4650 \r
4651     case IDM_GeneralOptions:\r
4652       GeneralOptionsPopup(hwnd);\r
4653       DrawPosition(TRUE, NULL);\r
4654       break;\r
4655 \r
4656     case IDM_BoardOptions:\r
4657       BoardOptionsPopup(hwnd);\r
4658       break;\r
4659 \r
4660     case IDM_EnginePlayOptions:\r
4661       EnginePlayOptionsPopup(hwnd);\r
4662       break;\r
4663 \r
4664     case IDM_Engine1Options:\r
4665       EngineOptionsPopup(hwnd, &first);\r
4666       break;\r
4667 \r
4668     case IDM_Engine2Options:\r
4669       EngineOptionsPopup(hwnd, &second);\r
4670       break;\r
4671 \r
4672     case IDM_OptionsUCI:\r
4673       UciOptionsPopup(hwnd);\r
4674       break;\r
4675 \r
4676     case IDM_IcsOptions:\r
4677       IcsOptionsPopup(hwnd);\r
4678       break;\r
4679 \r
4680     case IDM_Fonts:\r
4681       FontsOptionsPopup(hwnd);\r
4682       break;\r
4683 \r
4684     case IDM_Sounds:\r
4685       SoundOptionsPopup(hwnd);\r
4686       break;\r
4687 \r
4688     case IDM_CommPort:\r
4689       CommPortOptionsPopup(hwnd);\r
4690       break;\r
4691 \r
4692     case IDM_LoadOptions:\r
4693       LoadOptionsPopup(hwnd);\r
4694       break;\r
4695 \r
4696     case IDM_SaveOptions:\r
4697       SaveOptionsPopup(hwnd);\r
4698       break;\r
4699 \r
4700     case IDM_TimeControl:\r
4701       TimeControlOptionsPopup(hwnd);\r
4702       break;\r
4703 \r
4704     case IDM_SaveSettings:\r
4705       SaveSettings(settingsFileName);\r
4706       break;\r
4707 \r
4708     case IDM_SaveSettingsOnExit:\r
4709       saveSettingsOnExit = !saveSettingsOnExit;\r
4710       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4711                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4712                                          MF_CHECKED : MF_UNCHECKED));\r
4713       break;\r
4714 \r
4715     case IDM_Hint:\r
4716       HintEvent();\r
4717       break;\r
4718 \r
4719     case IDM_Book:\r
4720       BookEvent();\r
4721       break;\r
4722 \r
4723     case IDM_AboutGame:\r
4724       AboutGameEvent();\r
4725       break;\r
4726 \r
4727     case IDM_Debug:\r
4728       appData.debugMode = !appData.debugMode;\r
4729       if (appData.debugMode) {\r
4730         char dir[MSG_SIZ];\r
4731         GetCurrentDirectory(MSG_SIZ, dir);\r
4732         SetCurrentDirectory(installDir);\r
4733         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4734         SetCurrentDirectory(dir);\r
4735         setbuf(debugFP, NULL);\r
4736       } else {\r
4737         fclose(debugFP);\r
4738         debugFP = NULL;\r
4739       }\r
4740       break;\r
4741 \r
4742     case IDM_HELPCONTENTS:\r
4743       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4744           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4745           MessageBox (GetFocus(),\r
4746                     "Unable to activate help",\r
4747                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4748       }\r
4749       break;\r
4750 \r
4751     case IDM_HELPSEARCH:\r
4752         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4753             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4754         MessageBox (GetFocus(),\r
4755                     "Unable to activate help",\r
4756                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4757       }\r
4758       break;\r
4759 \r
4760     case IDM_HELPHELP:\r
4761       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4762         MessageBox (GetFocus(),\r
4763                     "Unable to activate help",\r
4764                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4765       }\r
4766       break;\r
4767 \r
4768     case IDM_ABOUT:\r
4769       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4770       DialogBox(hInst, \r
4771         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4772         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4773       FreeProcInstance(lpProc);\r
4774       break;\r
4775 \r
4776     case IDM_DirectCommand1:\r
4777       AskQuestionEvent("Direct Command",\r
4778                        "Send to chess program:", "", "1");\r
4779       break;\r
4780     case IDM_DirectCommand2:\r
4781       AskQuestionEvent("Direct Command",\r
4782                        "Send to second chess program:", "", "2");\r
4783       break;\r
4784 \r
4785     case EP_WhitePawn:\r
4786       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4787       fromX = fromY = -1;\r
4788       break;\r
4789 \r
4790     case EP_WhiteKnight:\r
4791       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4792       fromX = fromY = -1;\r
4793       break;\r
4794 \r
4795     case EP_WhiteBishop:\r
4796       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4797       fromX = fromY = -1;\r
4798       break;\r
4799 \r
4800     case EP_WhiteRook:\r
4801       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4802       fromX = fromY = -1;\r
4803       break;\r
4804 \r
4805     case EP_WhiteQueen:\r
4806       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4807       fromX = fromY = -1;\r
4808       break;\r
4809 \r
4810     case EP_WhiteFerz:\r
4811       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4812       fromX = fromY = -1;\r
4813       break;\r
4814 \r
4815     case EP_WhiteWazir:\r
4816       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4817       fromX = fromY = -1;\r
4818       break;\r
4819 \r
4820     case EP_WhiteAlfil:\r
4821       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4822       fromX = fromY = -1;\r
4823       break;\r
4824 \r
4825     case EP_WhiteCannon:\r
4826       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4827       fromX = fromY = -1;\r
4828       break;\r
4829 \r
4830     case EP_WhiteCardinal:\r
4831       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4832       fromX = fromY = -1;\r
4833       break;\r
4834 \r
4835     case EP_WhiteMarshall:\r
4836       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4837       fromX = fromY = -1;\r
4838       break;\r
4839 \r
4840     case EP_WhiteKing:\r
4841       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4842       fromX = fromY = -1;\r
4843       break;\r
4844 \r
4845     case EP_BlackPawn:\r
4846       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4847       fromX = fromY = -1;\r
4848       break;\r
4849 \r
4850     case EP_BlackKnight:\r
4851       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4852       fromX = fromY = -1;\r
4853       break;\r
4854 \r
4855     case EP_BlackBishop:\r
4856       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4857       fromX = fromY = -1;\r
4858       break;\r
4859 \r
4860     case EP_BlackRook:\r
4861       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4862       fromX = fromY = -1;\r
4863       break;\r
4864 \r
4865     case EP_BlackQueen:\r
4866       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4867       fromX = fromY = -1;\r
4868       break;\r
4869 \r
4870     case EP_BlackFerz:\r
4871       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4872       fromX = fromY = -1;\r
4873       break;\r
4874 \r
4875     case EP_BlackWazir:\r
4876       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
4877       fromX = fromY = -1;\r
4878       break;\r
4879 \r
4880     case EP_BlackAlfil:\r
4881       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
4882       fromX = fromY = -1;\r
4883       break;\r
4884 \r
4885     case EP_BlackCannon:\r
4886       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
4887       fromX = fromY = -1;\r
4888       break;\r
4889 \r
4890     case EP_BlackCardinal:\r
4891       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
4892       fromX = fromY = -1;\r
4893       break;\r
4894 \r
4895     case EP_BlackMarshall:\r
4896       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
4897       fromX = fromY = -1;\r
4898       break;\r
4899 \r
4900     case EP_BlackKing:\r
4901       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
4902       fromX = fromY = -1;\r
4903       break;\r
4904 \r
4905     case EP_EmptySquare:\r
4906       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
4907       fromX = fromY = -1;\r
4908       break;\r
4909 \r
4910     case EP_ClearBoard:\r
4911       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
4912       fromX = fromY = -1;\r
4913       break;\r
4914 \r
4915     case EP_White:\r
4916       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
4917       fromX = fromY = -1;\r
4918       break;\r
4919 \r
4920     case EP_Black:\r
4921       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
4922       fromX = fromY = -1;\r
4923       break;\r
4924 \r
4925     case EP_Promote:\r
4926       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
4927       fromX = fromY = -1;\r
4928       break;\r
4929 \r
4930     case EP_Demote:\r
4931       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
4932       fromX = fromY = -1;\r
4933       break;\r
4934 \r
4935     case DP_Pawn:\r
4936       DropMenuEvent(WhitePawn, fromX, fromY);\r
4937       fromX = fromY = -1;\r
4938       break;\r
4939 \r
4940     case DP_Knight:\r
4941       DropMenuEvent(WhiteKnight, fromX, fromY);\r
4942       fromX = fromY = -1;\r
4943       break;\r
4944 \r
4945     case DP_Bishop:\r
4946       DropMenuEvent(WhiteBishop, fromX, fromY);\r
4947       fromX = fromY = -1;\r
4948       break;\r
4949 \r
4950     case DP_Rook:\r
4951       DropMenuEvent(WhiteRook, fromX, fromY);\r
4952       fromX = fromY = -1;\r
4953       break;\r
4954 \r
4955     case DP_Queen:\r
4956       DropMenuEvent(WhiteQueen, fromX, fromY);\r
4957       fromX = fromY = -1;\r
4958       break;\r
4959 \r
4960     default:\r
4961       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4962     }\r
4963     break;\r
4964 \r
4965   case WM_TIMER:\r
4966     switch (wParam) {\r
4967     case CLOCK_TIMER_ID:\r
4968       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
4969       clockTimerEvent = 0;\r
4970       DecrementClocks(); /* call into back end */\r
4971       break;\r
4972     case LOAD_GAME_TIMER_ID:\r
4973       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
4974       loadGameTimerEvent = 0;\r
4975       AutoPlayGameLoop(); /* call into back end */\r
4976       break;\r
4977     case ANALYSIS_TIMER_ID:\r
4978       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
4979                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
4980         AnalysisPeriodicEvent(0);\r
4981       } else {\r
4982         KillTimer(hwnd, analysisTimerEvent);\r
4983         analysisTimerEvent = 0;\r
4984       }\r
4985       break;\r
4986     case DELAYED_TIMER_ID:\r
4987       KillTimer(hwnd, delayedTimerEvent);\r
4988       delayedTimerEvent = 0;\r
4989       delayedTimerCallback();\r
4990       break;\r
4991     }\r
4992     break;\r
4993 \r
4994   case WM_USER_Input:\r
4995     InputEvent(hwnd, message, wParam, lParam);\r
4996     break;\r
4997 \r
4998   /* [AS] Also move "attached" child windows */\r
4999   case WM_WINDOWPOSCHANGING:\r
5000 \r
5001     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5002         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5003 \r
5004         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5005             /* Window is moving */\r
5006             RECT rcMain;\r
5007 \r
5008 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5009             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5010             rcMain.right  = wpMain.x + wpMain.width;\r
5011             rcMain.top    = wpMain.y;\r
5012             rcMain.bottom = wpMain.y + wpMain.height;\r
5013             \r
5014             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5015             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5016             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5017             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5018             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5019             wpMain.x = lpwp->x;\r
5020             wpMain.y = lpwp->y;\r
5021         }\r
5022     }\r
5023     break;\r
5024 \r
5025   /* [AS] Snapping */\r
5026   case WM_ENTERSIZEMOVE:\r
5027     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5028     if (hwnd == hwndMain) {\r
5029       doingSizing = TRUE;\r
5030       lastSizing = 0;\r
5031     }\r
5032     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5033     break;\r
5034 \r
5035   case WM_SIZING:\r
5036     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5037     if (hwnd == hwndMain) {\r
5038       lastSizing = wParam;\r
5039     }\r
5040     break;\r
5041 \r
5042   case WM_MOVING:\r
5043     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5044       return OnMoving( &sd, hwnd, wParam, lParam );\r
5045 \r
5046   case WM_EXITSIZEMOVE:\r
5047     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5048     if (hwnd == hwndMain) {\r
5049       RECT client;\r
5050       doingSizing = FALSE;\r
5051       InvalidateRect(hwnd, &boardRect, FALSE);\r
5052       GetClientRect(hwnd, &client);\r
5053       ResizeBoard(client.right, client.bottom, lastSizing);\r
5054       lastSizing = 0;\r
5055       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5056     }\r
5057     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5058     break;\r
5059 \r
5060   case WM_DESTROY: /* message: window being destroyed */\r
5061     PostQuitMessage(0);\r
5062     break;\r
5063 \r
5064   case WM_CLOSE:\r
5065     if (hwnd == hwndMain) {\r
5066       ExitEvent(0);\r
5067     }\r
5068     break;\r
5069 \r
5070   default:      /* Passes it on if unprocessed */\r
5071     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5072   }\r
5073   return 0;\r
5074 }\r
5075 \r
5076 /*---------------------------------------------------------------------------*\\r
5077  *\r
5078  * Misc utility routines\r
5079  *\r
5080 \*---------------------------------------------------------------------------*/\r
5081 \r
5082 /*\r
5083  * Decent random number generator, at least not as bad as Windows\r
5084  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5085  */\r
5086 unsigned int randstate;\r
5087 \r
5088 int\r
5089 myrandom(void)\r
5090 {\r
5091   randstate = randstate * 1664525 + 1013904223;\r
5092   return (int) randstate & 0x7fffffff;\r
5093 }\r
5094 \r
5095 void\r
5096 mysrandom(unsigned int seed)\r
5097 {\r
5098   randstate = seed;\r
5099 }\r
5100 \r
5101 \r
5102 /* \r
5103  * returns TRUE if user selects a different color, FALSE otherwise \r
5104  */\r
5105 \r
5106 BOOL\r
5107 ChangeColor(HWND hwnd, COLORREF *which)\r
5108 {\r
5109   static BOOL firstTime = TRUE;\r
5110   static DWORD customColors[16];\r
5111   CHOOSECOLOR cc;\r
5112   COLORREF newcolor;\r
5113   int i;\r
5114   ColorClass ccl;\r
5115 \r
5116   if (firstTime) {\r
5117     /* Make initial colors in use available as custom colors */\r
5118     /* Should we put the compiled-in defaults here instead? */\r
5119     i = 0;\r
5120     customColors[i++] = lightSquareColor & 0xffffff;\r
5121     customColors[i++] = darkSquareColor & 0xffffff;\r
5122     customColors[i++] = whitePieceColor & 0xffffff;\r
5123     customColors[i++] = blackPieceColor & 0xffffff;\r
5124     customColors[i++] = highlightSquareColor & 0xffffff;\r
5125     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5126 \r
5127     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5128       customColors[i++] = textAttribs[ccl].color;\r
5129     }\r
5130     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5131     firstTime = FALSE;\r
5132   }\r
5133 \r
5134   cc.lStructSize = sizeof(cc);\r
5135   cc.hwndOwner = hwnd;\r
5136   cc.hInstance = NULL;\r
5137   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5138   cc.lpCustColors = (LPDWORD) customColors;\r
5139   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5140 \r
5141   if (!ChooseColor(&cc)) return FALSE;\r
5142 \r
5143   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5144   if (newcolor == *which) return FALSE;\r
5145   *which = newcolor;\r
5146   return TRUE;\r
5147 \r
5148   /*\r
5149   InitDrawingColors();\r
5150   InvalidateRect(hwnd, &boardRect, FALSE);\r
5151   */\r
5152 }\r
5153 \r
5154 BOOLEAN\r
5155 MyLoadSound(MySound *ms)\r
5156 {\r
5157   BOOL ok = FALSE;\r
5158   struct stat st;\r
5159   FILE *f;\r
5160 \r
5161   if (ms->data) free(ms->data);\r
5162   ms->data = NULL;\r
5163 \r
5164   switch (ms->name[0]) {\r
5165   case NULLCHAR:\r
5166     /* Silence */\r
5167     ok = TRUE;\r
5168     break;\r
5169   case '$':\r
5170     /* System sound from Control Panel.  Don't preload here. */\r
5171     ok = TRUE;\r
5172     break;\r
5173   case '!':\r
5174     if (ms->name[1] == NULLCHAR) {\r
5175       /* "!" alone = silence */\r
5176       ok = TRUE;\r
5177     } else {\r
5178       /* Builtin wave resource.  Error if not found. */\r
5179       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5180       if (h == NULL) break;\r
5181       ms->data = (void *)LoadResource(hInst, h);\r
5182       if (h == NULL) break;\r
5183       ok = TRUE;\r
5184     }\r
5185     break;\r
5186   default:\r
5187     /* .wav file.  Error if not found. */\r
5188     f = fopen(ms->name, "rb");\r
5189     if (f == NULL) break;\r
5190     if (fstat(fileno(f), &st) < 0) break;\r
5191     ms->data = malloc(st.st_size);\r
5192     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5193     fclose(f);\r
5194     ok = TRUE;\r
5195     break;\r
5196   }\r
5197   if (!ok) {\r
5198     char buf[MSG_SIZ];\r
5199     sprintf(buf, "Error loading sound %s", ms->name);\r
5200     DisplayError(buf, GetLastError());\r
5201   }\r
5202   return ok;\r
5203 }\r
5204 \r
5205 BOOLEAN\r
5206 MyPlaySound(MySound *ms)\r
5207 {\r
5208   BOOLEAN ok = FALSE;\r
5209 \r
5210   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5211   switch (ms->name[0]) {\r
5212   case NULLCHAR:\r
5213         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5214     /* Silence */\r
5215     ok = TRUE;\r
5216     break;\r
5217   case '$':\r
5218     /* System sound from Control Panel (deprecated feature).\r
5219        "$" alone or an unset sound name gets default beep (still in use). */\r
5220     if (ms->name[1]) {\r
5221       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5222     }\r
5223     if (!ok) ok = MessageBeep(MB_OK);\r
5224     break; \r
5225   case '!':\r
5226     /* Builtin wave resource, or "!" alone for silence */\r
5227     if (ms->name[1]) {\r
5228       if (ms->data == NULL) return FALSE;\r
5229       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5230     } else {\r
5231       ok = TRUE;\r
5232     }\r
5233     break;\r
5234   default:\r
5235     /* .wav file.  Error if not found. */\r
5236     if (ms->data == NULL) return FALSE;\r
5237     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5238     break;\r
5239   }\r
5240   /* Don't print an error: this can happen innocently if the sound driver\r
5241      is busy; for instance, if another instance of WinBoard is playing\r
5242      a sound at about the same time. */\r
5243   return ok;\r
5244 }\r
5245 \r
5246 \r
5247 LRESULT CALLBACK\r
5248 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5249 {\r
5250   BOOL ok;\r
5251   OPENFILENAME *ofn;\r
5252   static UINT *number; /* gross that this is static */\r
5253 \r
5254   switch (message) {\r
5255   case WM_INITDIALOG: /* message: initialize dialog box */\r
5256     /* Center the dialog over the application window */\r
5257     ofn = (OPENFILENAME *) lParam;\r
5258     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5259       number = (UINT *) ofn->lCustData;\r
5260       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5261     } else {\r
5262       number = NULL;\r
5263     }\r
5264     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5265     return FALSE;  /* Allow for further processing */\r
5266 \r
5267   case WM_COMMAND:\r
5268     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5269       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5270     }\r
5271     return FALSE;  /* Allow for further processing */\r
5272   }\r
5273   return FALSE;\r
5274 }\r
5275 \r
5276 UINT APIENTRY\r
5277 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5278 {\r
5279   static UINT *number;\r
5280   OPENFILENAME *ofname;\r
5281   OFNOTIFY *ofnot;\r
5282   switch (uiMsg) {\r
5283   case WM_INITDIALOG:\r
5284     ofname = (OPENFILENAME *)lParam;\r
5285     number = (UINT *)(ofname->lCustData);\r
5286     break;\r
5287   case WM_NOTIFY:\r
5288     ofnot = (OFNOTIFY *)lParam;\r
5289     if (ofnot->hdr.code == CDN_FILEOK) {\r
5290       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5291     }\r
5292     break;\r
5293   }\r
5294   return 0;\r
5295 }\r
5296 \r
5297 \r
5298 FILE *\r
5299 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5300                char *nameFilt, char *dlgTitle, UINT *number,\r
5301                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5302 {\r
5303   OPENFILENAME openFileName;\r
5304   char buf1[MSG_SIZ];\r
5305   FILE *f;\r
5306 \r
5307   if (fileName == NULL) fileName = buf1;\r
5308   if (defName == NULL) {\r
5309     strcpy(fileName, "*.");\r
5310     strcat(fileName, defExt);\r
5311   } else {\r
5312     strcpy(fileName, defName);\r
5313   }\r
5314   if (fileTitle) strcpy(fileTitle, "");\r
5315   if (number) *number = 0;\r
5316 \r
5317   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5318   openFileName.hwndOwner         = hwnd;\r
5319   openFileName.hInstance         = (HANDLE) hInst;\r
5320   openFileName.lpstrFilter       = nameFilt;\r
5321   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5322   openFileName.nMaxCustFilter    = 0L;\r
5323   openFileName.nFilterIndex      = 1L;\r
5324   openFileName.lpstrFile         = fileName;\r
5325   openFileName.nMaxFile          = MSG_SIZ;\r
5326   openFileName.lpstrFileTitle    = fileTitle;\r
5327   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5328   openFileName.lpstrInitialDir   = NULL;\r
5329   openFileName.lpstrTitle        = dlgTitle;\r
5330   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5331     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5332     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5333     | (oldDialog ? 0 : OFN_EXPLORER);\r
5334   openFileName.nFileOffset       = 0;\r
5335   openFileName.nFileExtension    = 0;\r
5336   openFileName.lpstrDefExt       = defExt;\r
5337   openFileName.lCustData         = (LONG) number;\r
5338   openFileName.lpfnHook          = oldDialog ?\r
5339     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5340   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5341 \r
5342   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5343                         GetOpenFileName(&openFileName)) {\r
5344     /* open the file */\r
5345     f = fopen(openFileName.lpstrFile, write);\r
5346     if (f == NULL) {\r
5347       MessageBox(hwnd, "File open failed", NULL,\r
5348                  MB_OK|MB_ICONEXCLAMATION);\r
5349       return NULL;\r
5350     }\r
5351   } else {\r
5352     int err = CommDlgExtendedError();\r
5353     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5354     return FALSE;\r
5355   }\r
5356   return f;\r
5357 }\r
5358 \r
5359 \r
5360 \r
5361 VOID APIENTRY\r
5362 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5363 {\r
5364   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5365 \r
5366   /*\r
5367    * Get the first pop-up menu in the menu template. This is the\r
5368    * menu that TrackPopupMenu displays.\r
5369    */\r
5370   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5371 \r
5372   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5373 \r
5374   /*\r
5375    * TrackPopup uses screen coordinates, so convert the\r
5376    * coordinates of the mouse click to screen coordinates.\r
5377    */\r
5378   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5379 \r
5380   /* Draw and track the floating pop-up menu. */\r
5381   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5382                  pt.x, pt.y, 0, hwnd, NULL);\r
5383 \r
5384   /* Destroy the menu.*/\r
5385   DestroyMenu(hmenu);\r
5386 }\r
5387    \r
5388 typedef struct {\r
5389   HWND hDlg, hText;\r
5390   int sizeX, sizeY, newSizeX, newSizeY;\r
5391   HDWP hdwp;\r
5392 } ResizeEditPlusButtonsClosure;\r
5393 \r
5394 BOOL CALLBACK\r
5395 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5396 {\r
5397   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5398   RECT rect;\r
5399   POINT pt;\r
5400 \r
5401   if (hChild == cl->hText) return TRUE;\r
5402   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5403   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5404   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5405   ScreenToClient(cl->hDlg, &pt);\r
5406   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5407     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5408   return TRUE;\r
5409 }\r
5410 \r
5411 /* Resize a dialog that has a (rich) edit field filling most of\r
5412    the top, with a row of buttons below */\r
5413 VOID\r
5414 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5415 {\r
5416   RECT rectText;\r
5417   int newTextHeight, newTextWidth;\r
5418   ResizeEditPlusButtonsClosure cl;\r
5419   \r
5420   /*if (IsIconic(hDlg)) return;*/\r
5421   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5422   \r
5423   cl.hdwp = BeginDeferWindowPos(8);\r
5424 \r
5425   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5426   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5427   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5428   if (newTextHeight < 0) {\r
5429     newSizeY += -newTextHeight;\r
5430     newTextHeight = 0;\r
5431   }\r
5432   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5433     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5434 \r
5435   cl.hDlg = hDlg;\r
5436   cl.hText = hText;\r
5437   cl.sizeX = sizeX;\r
5438   cl.sizeY = sizeY;\r
5439   cl.newSizeX = newSizeX;\r
5440   cl.newSizeY = newSizeY;\r
5441   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5442 \r
5443   EndDeferWindowPos(cl.hdwp);\r
5444 }\r
5445 \r
5446 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5447 {\r
5448     RECT    rChild, rParent;\r
5449     int     wChild, hChild, wParent, hParent;\r
5450     int     wScreen, hScreen, xNew, yNew;\r
5451     HDC     hdc;\r
5452 \r
5453     /* Get the Height and Width of the child window */\r
5454     GetWindowRect (hwndChild, &rChild);\r
5455     wChild = rChild.right - rChild.left;\r
5456     hChild = rChild.bottom - rChild.top;\r
5457 \r
5458     /* Get the Height and Width of the parent window */\r
5459     GetWindowRect (hwndParent, &rParent);\r
5460     wParent = rParent.right - rParent.left;\r
5461     hParent = rParent.bottom - rParent.top;\r
5462 \r
5463     /* Get the display limits */\r
5464     hdc = GetDC (hwndChild);\r
5465     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5466     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5467     ReleaseDC(hwndChild, hdc);\r
5468 \r
5469     /* Calculate new X position, then adjust for screen */\r
5470     xNew = rParent.left + ((wParent - wChild) /2);\r
5471     if (xNew < 0) {\r
5472         xNew = 0;\r
5473     } else if ((xNew+wChild) > wScreen) {\r
5474         xNew = wScreen - wChild;\r
5475     }\r
5476 \r
5477     /* Calculate new Y position, then adjust for screen */\r
5478     if( mode == 0 ) {\r
5479         yNew = rParent.top  + ((hParent - hChild) /2);\r
5480     }\r
5481     else {\r
5482         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5483     }\r
5484 \r
5485     if (yNew < 0) {\r
5486         yNew = 0;\r
5487     } else if ((yNew+hChild) > hScreen) {\r
5488         yNew = hScreen - hChild;\r
5489     }\r
5490 \r
5491     /* Set it, and return */\r
5492     return SetWindowPos (hwndChild, NULL,\r
5493                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5494 }\r
5495 \r
5496 /* Center one window over another */\r
5497 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5498 {\r
5499     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5500 }\r
5501 \r
5502 /*---------------------------------------------------------------------------*\\r
5503  *\r
5504  * Startup Dialog functions\r
5505  *\r
5506 \*---------------------------------------------------------------------------*/\r
5507 void\r
5508 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5509 {\r
5510   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5511 \r
5512   while (*cd != NULL) {\r
5513     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5514     cd++;\r
5515   }\r
5516 }\r
5517 \r
5518 void\r
5519 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5520 {\r
5521   char buf1[MAX_ARG_LEN];\r
5522   int len;\r
5523 \r
5524   if (str[0] == '@') {\r
5525     FILE* f = fopen(str + 1, "r");\r
5526     if (f == NULL) {\r
5527       DisplayFatalError(str + 1, errno, 2);\r
5528       return;\r
5529     }\r
5530     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5531     fclose(f);\r
5532     buf1[len] = NULLCHAR;\r
5533     str = buf1;\r
5534   }\r
5535 \r
5536   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5537 \r
5538   for (;;) {\r
5539     char buf[MSG_SIZ];\r
5540     char *end = strchr(str, '\n');\r
5541     if (end == NULL) return;\r
5542     memcpy(buf, str, end - str);\r
5543     buf[end - str] = NULLCHAR;\r
5544     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5545     str = end + 1;\r
5546   }\r
5547 }\r
5548 \r
5549 void\r
5550 SetStartupDialogEnables(HWND hDlg)\r
5551 {\r
5552   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5553     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5554     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5555   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5556     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5557   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5558     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5559   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5560     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5561   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5562     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5563     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5564     IsDlgButtonChecked(hDlg, OPT_View));\r
5565 }\r
5566 \r
5567 char *\r
5568 QuoteForFilename(char *filename)\r
5569 {\r
5570   int dquote, space;\r
5571   dquote = strchr(filename, '"') != NULL;\r
5572   space = strchr(filename, ' ') != NULL;\r
5573   if (dquote || space) {\r
5574     if (dquote) {\r
5575       return "'";\r
5576     } else {\r
5577       return "\"";\r
5578     }\r
5579   } else {\r
5580     return "";\r
5581   }\r
5582 }\r
5583 \r
5584 VOID\r
5585 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5586 {\r
5587   char buf[MSG_SIZ];\r
5588   char *q;\r
5589 \r
5590   InitComboStringsFromOption(hwndCombo, nthnames);\r
5591   q = QuoteForFilename(nthcp);\r
5592   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5593   if (*nthdir != NULLCHAR) {\r
5594     q = QuoteForFilename(nthdir);\r
5595     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5596   }\r
5597   if (*nthcp == NULLCHAR) {\r
5598     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5599   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5600     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5601     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5602   }\r
5603 }\r
5604 \r
5605 LRESULT CALLBACK\r
5606 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5607 {\r
5608   char buf[MSG_SIZ];\r
5609   HANDLE hwndCombo;\r
5610   char *p;\r
5611 \r
5612   switch (message) {\r
5613   case WM_INITDIALOG:\r
5614     /* Center the dialog */\r
5615     CenterWindow (hDlg, GetDesktopWindow());\r
5616     /* Initialize the dialog items */\r
5617     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5618                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5619                   firstChessProgramNames);\r
5620     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5621                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5622                   secondChessProgramNames);\r
5623     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5624     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5625     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5626     if (*appData.icsHelper != NULLCHAR) {\r
5627       char *q = QuoteForFilename(appData.icsHelper);\r
5628       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5629     }\r
5630     if (*appData.icsHost == NULLCHAR) {\r
5631       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5632       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5633     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5634       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5635       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5636     }\r
5637 \r
5638     if (appData.icsActive) {\r
5639       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5640     }\r
5641     else if (appData.noChessProgram) {\r
5642       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5643     }\r
5644     else {\r
5645       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5646     }\r
5647 \r
5648     SetStartupDialogEnables(hDlg);\r
5649     return TRUE;\r
5650 \r
5651   case WM_COMMAND:\r
5652     switch (LOWORD(wParam)) {\r
5653     case IDOK:\r
5654       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5655         strcpy(buf, "/fcp=");\r
5656         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5657         p = buf;\r
5658         ParseArgs(StringGet, &p);\r
5659         strcpy(buf, "/scp=");\r
5660         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5661         p = buf;\r
5662         ParseArgs(StringGet, &p);\r
5663         appData.noChessProgram = FALSE;\r
5664         appData.icsActive = FALSE;\r
5665       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5666         strcpy(buf, "/ics /icshost=");\r
5667         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5668         p = buf;\r
5669         ParseArgs(StringGet, &p);\r
5670         if (appData.zippyPlay) {\r
5671           strcpy(buf, "/fcp=");\r
5672           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5673           p = buf;\r
5674           ParseArgs(StringGet, &p);\r
5675         }\r
5676       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5677         appData.noChessProgram = TRUE;\r
5678         appData.icsActive = FALSE;\r
5679       } else {\r
5680         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5681                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5682         return TRUE;\r
5683       }\r
5684       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5685         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5686         p = buf;\r
5687         ParseArgs(StringGet, &p);\r
5688       }\r
5689       EndDialog(hDlg, TRUE);\r
5690       return TRUE;\r
5691 \r
5692     case IDCANCEL:\r
5693       ExitEvent(0);\r
5694       return TRUE;\r
5695 \r
5696     case IDM_HELPCONTENTS:\r
5697       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5698         MessageBox (GetFocus(),\r
5699                     "Unable to activate help",\r
5700                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5701       }\r
5702       break;\r
5703 \r
5704     default:\r
5705       SetStartupDialogEnables(hDlg);\r
5706       break;\r
5707     }\r
5708     break;\r
5709   }\r
5710   return FALSE;\r
5711 }\r
5712 \r
5713 /*---------------------------------------------------------------------------*\\r
5714  *\r
5715  * About box dialog functions\r
5716  *\r
5717 \*---------------------------------------------------------------------------*/\r
5718 \r
5719 /* Process messages for "About" dialog box */\r
5720 LRESULT CALLBACK\r
5721 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5722 {\r
5723   switch (message) {\r
5724   case WM_INITDIALOG: /* message: initialize dialog box */\r
5725     /* Center the dialog over the application window */\r
5726     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5727     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5728     JAWS_COPYRIGHT\r
5729     return (TRUE);\r
5730 \r
5731   case WM_COMMAND: /* message: received a command */\r
5732     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5733         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5734       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5735       return (TRUE);\r
5736     }\r
5737     break;\r
5738   }\r
5739   return (FALSE);\r
5740 }\r
5741 \r
5742 /*---------------------------------------------------------------------------*\\r
5743  *\r
5744  * Comment Dialog functions\r
5745  *\r
5746 \*---------------------------------------------------------------------------*/\r
5747 \r
5748 LRESULT CALLBACK\r
5749 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5750 {\r
5751   static HANDLE hwndText = NULL;\r
5752   int len, newSizeX, newSizeY, flags;\r
5753   static int sizeX, sizeY;\r
5754   char *str;\r
5755   RECT rect;\r
5756   MINMAXINFO *mmi;\r
5757 \r
5758   switch (message) {\r
5759   case WM_INITDIALOG: /* message: initialize dialog box */\r
5760     /* Initialize the dialog items */\r
5761     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5762     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5763     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5764     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5765     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5766     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5767     SetWindowText(hDlg, commentTitle);\r
5768     if (editComment) {\r
5769       SetFocus(hwndText);\r
5770     } else {\r
5771       SetFocus(GetDlgItem(hDlg, IDOK));\r
5772     }\r
5773     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5774                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5775                 MAKELPARAM(FALSE, 0));\r
5776     /* Size and position the dialog */\r
5777     if (!commentDialog) {\r
5778       commentDialog = hDlg;\r
5779       flags = SWP_NOZORDER;\r
5780       GetClientRect(hDlg, &rect);\r
5781       sizeX = rect.right;\r
5782       sizeY = rect.bottom;\r
5783       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5784           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5785         WINDOWPLACEMENT wp;\r
5786         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5787         wp.length = sizeof(WINDOWPLACEMENT);\r
5788         wp.flags = 0;\r
5789         wp.showCmd = SW_SHOW;\r
5790         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5791         wp.rcNormalPosition.left = wpComment.x;\r
5792         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5793         wp.rcNormalPosition.top = wpComment.y;\r
5794         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5795         SetWindowPlacement(hDlg, &wp);\r
5796 \r
5797         GetClientRect(hDlg, &rect);\r
5798         newSizeX = rect.right;\r
5799         newSizeY = rect.bottom;\r
5800         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5801                               newSizeX, newSizeY);\r
5802         sizeX = newSizeX;\r
5803         sizeY = newSizeY;\r
5804       }\r
5805     }\r
5806     return FALSE;\r
5807 \r
5808   case WM_COMMAND: /* message: received a command */\r
5809     switch (LOWORD(wParam)) {\r
5810     case IDOK:\r
5811       if (editComment) {\r
5812         char *p, *q;\r
5813         /* Read changed options from the dialog box */\r
5814         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5815         len = GetWindowTextLength(hwndText);\r
5816         str = (char *) malloc(len + 1);\r
5817         GetWindowText(hwndText, str, len + 1);\r
5818         p = q = str;\r
5819         while (*q) {\r
5820           if (*q == '\r')\r
5821             q++;\r
5822           else\r
5823             *p++ = *q++;\r
5824         }\r
5825         *p = NULLCHAR;\r
5826         ReplaceComment(commentIndex, str);\r
5827         free(str);\r
5828       }\r
5829       CommentPopDown();\r
5830       return TRUE;\r
5831 \r
5832     case IDCANCEL:\r
5833     case OPT_CancelComment:\r
5834       CommentPopDown();\r
5835       return TRUE;\r
5836 \r
5837     case OPT_ClearComment:\r
5838       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5839       break;\r
5840 \r
5841     case OPT_EditComment:\r
5842       EditCommentEvent();\r
5843       return TRUE;\r
5844 \r
5845     default:\r
5846       break;\r
5847     }\r
5848     break;\r
5849 \r
5850   case WM_SIZE:\r
5851     newSizeX = LOWORD(lParam);\r
5852     newSizeY = HIWORD(lParam);\r
5853     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
5854     sizeX = newSizeX;\r
5855     sizeY = newSizeY;\r
5856     break;\r
5857 \r
5858   case WM_GETMINMAXINFO:\r
5859     /* Prevent resizing window too small */\r
5860     mmi = (MINMAXINFO *) lParam;\r
5861     mmi->ptMinTrackSize.x = 100;\r
5862     mmi->ptMinTrackSize.y = 100;\r
5863     break;\r
5864   }\r
5865   return FALSE;\r
5866 }\r
5867 \r
5868 VOID\r
5869 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
5870 {\r
5871   FARPROC lpProc;\r
5872   char *p, *q;\r
5873 \r
5874   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
5875 \r
5876   if (str == NULL) str = "";\r
5877   p = (char *) malloc(2 * strlen(str) + 2);\r
5878   q = p;\r
5879   while (*str) {\r
5880     if (*str == '\n') *q++ = '\r';\r
5881     *q++ = *str++;\r
5882   }\r
5883   *q = NULLCHAR;\r
5884   if (commentText != NULL) free(commentText);\r
5885 \r
5886   commentIndex = index;\r
5887   commentTitle = title;\r
5888   commentText = p;\r
5889   editComment = edit;\r
5890 \r
5891   if (commentDialog) {\r
5892     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
5893     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
5894   } else {\r
5895     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
5896     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
5897                  hwndMain, (DLGPROC)lpProc);\r
5898     FreeProcInstance(lpProc);\r
5899   }\r
5900   commentUp = TRUE;\r
5901 }\r
5902 \r
5903 \r
5904 /*---------------------------------------------------------------------------*\\r
5905  *\r
5906  * Type-in move dialog functions\r
5907  * \r
5908 \*---------------------------------------------------------------------------*/\r
5909 \r
5910 LRESULT CALLBACK\r
5911 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5912 {\r
5913   char move[MSG_SIZ];\r
5914   HWND hInput;\r
5915   ChessMove moveType;\r
5916   int fromX, fromY, toX, toY;\r
5917   char promoChar;\r
5918 \r
5919   switch (message) {\r
5920   case WM_INITDIALOG:\r
5921     move[0] = (char) lParam;\r
5922     move[1] = NULLCHAR;\r
5923     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5924     hInput = GetDlgItem(hDlg, OPT_Move);\r
5925     SetWindowText(hInput, move);\r
5926     SetFocus(hInput);\r
5927     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5928     return FALSE;\r
5929 \r
5930   case WM_COMMAND:\r
5931     switch (LOWORD(wParam)) {\r
5932     case IDOK:\r
5933       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
5934       { int n; Board board;\r
5935         // [HGM] FENedit\r
5936         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
5937                 EditPositionPasteFEN(move);\r
5938                 EndDialog(hDlg, TRUE);\r
5939                 return TRUE;\r
5940         }\r
5941         // [HGM] movenum: allow move number to be typed in any mode\r
5942         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
5943           ToNrEvent(2*n-1);\r
5944           EndDialog(hDlg, TRUE);\r
5945           return TRUE;\r
5946         }\r
5947       }\r
5948       if (gameMode != EditGame && currentMove != forwardMostMove && \r
5949         gameMode != Training) {\r
5950         DisplayMoveError("Displayed move is not current");\r
5951       } else {\r
5952 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
5953         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5954           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
5955         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
5956         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5957           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
5958           if (gameMode != Training)\r
5959               forwardMostMove = currentMove;\r
5960           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
5961         } else {\r
5962           DisplayMoveError("Could not parse move");\r
5963         }\r
5964       }\r
5965       EndDialog(hDlg, TRUE);\r
5966       return TRUE;\r
5967     case IDCANCEL:\r
5968       EndDialog(hDlg, FALSE);\r
5969       return TRUE;\r
5970     default:\r
5971       break;\r
5972     }\r
5973     break;\r
5974   }\r
5975   return FALSE;\r
5976 }\r
5977 \r
5978 VOID\r
5979 PopUpMoveDialog(char firstchar)\r
5980 {\r
5981     FARPROC lpProc;\r
5982     \r
5983     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
5984         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
5985         gameMode == AnalyzeMode || gameMode == EditGame || \r
5986         gameMode == EditPosition || gameMode == IcsExamining ||\r
5987         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
5988         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
5989                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
5990                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
5991         gameMode == Training) {\r
5992       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
5993       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
5994         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
5995       FreeProcInstance(lpProc);\r
5996     }\r
5997 }\r
5998 \r
5999 /*---------------------------------------------------------------------------*\\r
6000  *\r
6001  * Type-in name dialog functions\r
6002  * \r
6003 \*---------------------------------------------------------------------------*/\r
6004 \r
6005 LRESULT CALLBACK\r
6006 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6007 {\r
6008   char move[MSG_SIZ];\r
6009   HWND hInput;\r
6010 \r
6011   switch (message) {\r
6012   case WM_INITDIALOG:\r
6013     move[0] = (char) lParam;\r
6014     move[1] = NULLCHAR;\r
6015     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6016     hInput = GetDlgItem(hDlg, OPT_Name);\r
6017     SetWindowText(hInput, move);\r
6018     SetFocus(hInput);\r
6019     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6020     return FALSE;\r
6021 \r
6022   case WM_COMMAND:\r
6023     switch (LOWORD(wParam)) {\r
6024     case IDOK:\r
6025       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6026       appData.userName = strdup(move);\r
6027       SetUserLogo();\r
6028 \r
6029       EndDialog(hDlg, TRUE);\r
6030       return TRUE;\r
6031     case IDCANCEL:\r
6032       EndDialog(hDlg, FALSE);\r
6033       return TRUE;\r
6034     default:\r
6035       break;\r
6036     }\r
6037     break;\r
6038   }\r
6039   return FALSE;\r
6040 }\r
6041 \r
6042 VOID\r
6043 PopUpNameDialog(char firstchar)\r
6044 {\r
6045     FARPROC lpProc;\r
6046     \r
6047       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6048       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6049         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6050       FreeProcInstance(lpProc);\r
6051 }\r
6052 \r
6053 /*---------------------------------------------------------------------------*\\r
6054  *\r
6055  *  Error dialogs\r
6056  * \r
6057 \*---------------------------------------------------------------------------*/\r
6058 \r
6059 /* Nonmodal error box */\r
6060 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6061                              WPARAM wParam, LPARAM lParam);\r
6062 \r
6063 VOID\r
6064 ErrorPopUp(char *title, char *content)\r
6065 {\r
6066   FARPROC lpProc;\r
6067   char *p, *q;\r
6068   BOOLEAN modal = hwndMain == NULL;\r
6069 \r
6070   p = content;\r
6071   q = errorMessage;\r
6072   while (*p) {\r
6073     if (*p == '\n') {\r
6074       if (modal) {\r
6075         *q++ = ' ';\r
6076         p++;\r
6077       } else {\r
6078         *q++ = '\r';\r
6079         *q++ = *p++;\r
6080       }\r
6081     } else {\r
6082       *q++ = *p++;\r
6083     }\r
6084   }\r
6085   *q = NULLCHAR;\r
6086   strncpy(errorTitle, title, sizeof(errorTitle));\r
6087   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6088   \r
6089   if (modal) {\r
6090     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6091   } else {\r
6092     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6093     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6094                  hwndMain, (DLGPROC)lpProc);\r
6095     FreeProcInstance(lpProc);\r
6096   }\r
6097 }\r
6098 \r
6099 VOID\r
6100 ErrorPopDown()\r
6101 {\r
6102   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6103   if (errorDialog == NULL) return;\r
6104   DestroyWindow(errorDialog);\r
6105   errorDialog = NULL;\r
6106   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6107 }\r
6108 \r
6109 LRESULT CALLBACK\r
6110 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6111 {\r
6112   HANDLE hwndText;\r
6113   RECT rChild;\r
6114 \r
6115   switch (message) {\r
6116   case WM_INITDIALOG:\r
6117     GetWindowRect(hDlg, &rChild);\r
6118 \r
6119     /*\r
6120     SetWindowPos(hDlg, NULL, rChild.left,\r
6121       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6122       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6123     */\r
6124 \r
6125     /* \r
6126         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6127         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6128         and it doesn't work when you resize the dialog.\r
6129         For now, just give it a default position.\r
6130     */\r
6131     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6132 \r
6133     errorDialog = hDlg;\r
6134     SetWindowText(hDlg, errorTitle);\r
6135     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6136     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6137     return FALSE;\r
6138 \r
6139   case WM_COMMAND:\r
6140     switch (LOWORD(wParam)) {\r
6141     case IDOK:\r
6142     case IDCANCEL:\r
6143       if (errorDialog == hDlg) errorDialog = NULL;\r
6144       DestroyWindow(hDlg);\r
6145       return TRUE;\r
6146 \r
6147     default:\r
6148       break;\r
6149     }\r
6150     break;\r
6151   }\r
6152   return FALSE;\r
6153 }\r
6154 \r
6155 #ifdef GOTHIC\r
6156 HWND gothicDialog = NULL;\r
6157 \r
6158 LRESULT CALLBACK\r
6159 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6160 {\r
6161   HANDLE hwndText;\r
6162   RECT rChild;\r
6163   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6164 \r
6165   switch (message) {\r
6166   case WM_INITDIALOG:\r
6167     GetWindowRect(hDlg, &rChild);\r
6168 \r
6169     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6170                                                              SWP_NOZORDER);\r
6171 \r
6172     /* \r
6173         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6174         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6175         and it doesn't work when you resize the dialog.\r
6176         For now, just give it a default position.\r
6177     */\r
6178     gothicDialog = hDlg;\r
6179     SetWindowText(hDlg, errorTitle);\r
6180     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6181     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6182     return FALSE;\r
6183 \r
6184   case WM_COMMAND:\r
6185     switch (LOWORD(wParam)) {\r
6186     case IDOK:\r
6187     case IDCANCEL:\r
6188       if (errorDialog == hDlg) errorDialog = NULL;\r
6189       DestroyWindow(hDlg);\r
6190       return TRUE;\r
6191 \r
6192     default:\r
6193       break;\r
6194     }\r
6195     break;\r
6196   }\r
6197   return FALSE;\r
6198 }\r
6199 \r
6200 VOID\r
6201 GothicPopUp(char *title, VariantClass variant)\r
6202 {\r
6203   FARPROC lpProc;\r
6204   static char *lastTitle;\r
6205 \r
6206   strncpy(errorTitle, title, sizeof(errorTitle));\r
6207   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6208 \r
6209   if(lastTitle != title && gothicDialog != NULL) {\r
6210     DestroyWindow(gothicDialog);\r
6211     gothicDialog = NULL;\r
6212   }\r
6213   if(variant != VariantNormal && gothicDialog == NULL) {\r
6214     title = lastTitle;\r
6215     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6216     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6217                  hwndMain, (DLGPROC)lpProc);\r
6218     FreeProcInstance(lpProc);\r
6219   }\r
6220 }\r
6221 #endif\r
6222 \r
6223 /*---------------------------------------------------------------------------*\\r
6224  *\r
6225  *  Ics Interaction console functions\r
6226  *\r
6227 \*---------------------------------------------------------------------------*/\r
6228 \r
6229 #define HISTORY_SIZE 64\r
6230 static char *history[HISTORY_SIZE];\r
6231 int histIn = 0, histP = 0;\r
6232 \r
6233 VOID\r
6234 SaveInHistory(char *cmd)\r
6235 {\r
6236   if (history[histIn] != NULL) {\r
6237     free(history[histIn]);\r
6238     history[histIn] = NULL;\r
6239   }\r
6240   if (*cmd == NULLCHAR) return;\r
6241   history[histIn] = StrSave(cmd);\r
6242   histIn = (histIn + 1) % HISTORY_SIZE;\r
6243   if (history[histIn] != NULL) {\r
6244     free(history[histIn]);\r
6245     history[histIn] = NULL;\r
6246   }\r
6247   histP = histIn;\r
6248 }\r
6249 \r
6250 char *\r
6251 PrevInHistory(char *cmd)\r
6252 {\r
6253   int newhp;\r
6254   if (histP == histIn) {\r
6255     if (history[histIn] != NULL) free(history[histIn]);\r
6256     history[histIn] = StrSave(cmd);\r
6257   }\r
6258   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6259   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6260   histP = newhp;\r
6261   return history[histP];\r
6262 }\r
6263 \r
6264 char *\r
6265 NextInHistory()\r
6266 {\r
6267   if (histP == histIn) return NULL;\r
6268   histP = (histP + 1) % HISTORY_SIZE;\r
6269   return history[histP];   \r
6270 }\r
6271 \r
6272 HMENU\r
6273 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6274 {\r
6275   HMENU hmenu, h;\r
6276   int i = 0;\r
6277   hmenu = LoadMenu(hInst, "TextMenu");\r
6278   h = GetSubMenu(hmenu, 0);\r
6279   while (e->item) {\r
6280     if (strcmp(e->item, "-") == 0) {\r
6281       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6282     } else {\r
6283       if (e->item[0] == '|') {\r
6284         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6285                    IDM_CommandX + i, &e->item[1]);\r
6286       } else {\r
6287         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6288       }\r
6289     }\r
6290     e++;\r
6291     i++;\r
6292   } \r
6293   return hmenu;\r
6294 }\r
6295 \r
6296 WNDPROC consoleTextWindowProc;\r
6297 \r
6298 void\r
6299 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6300 {\r
6301   char buf[MSG_SIZ], name[MSG_SIZ];\r
6302   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6303   CHARRANGE sel;\r
6304 \r
6305   if (!getname) {\r
6306     SetWindowText(hInput, command);\r
6307     if (immediate) {\r
6308       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6309     } else {\r
6310       sel.cpMin = 999999;\r
6311       sel.cpMax = 999999;\r
6312       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6313       SetFocus(hInput);\r
6314     }\r
6315     return;\r
6316   }    \r
6317   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6318   if (sel.cpMin == sel.cpMax) {\r
6319     /* Expand to surrounding word */\r
6320     TEXTRANGE tr;\r
6321     do {\r
6322       tr.chrg.cpMax = sel.cpMin;\r
6323       tr.chrg.cpMin = --sel.cpMin;\r
6324       if (sel.cpMin < 0) break;\r
6325       tr.lpstrText = name;\r
6326       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6327     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6328     sel.cpMin++;\r
6329 \r
6330     do {\r
6331       tr.chrg.cpMin = sel.cpMax;\r
6332       tr.chrg.cpMax = ++sel.cpMax;\r
6333       tr.lpstrText = name;\r
6334       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6335     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6336     sel.cpMax--;\r
6337 \r
6338     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6339       MessageBeep(MB_ICONEXCLAMATION);\r
6340       return;\r
6341     }\r
6342     tr.chrg = sel;\r
6343     tr.lpstrText = name;\r
6344     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6345   } else {\r
6346     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6347       MessageBeep(MB_ICONEXCLAMATION);\r
6348       return;\r
6349     }\r
6350     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6351   }\r
6352   if (immediate) {\r
6353     sprintf(buf, "%s %s", command, name);\r
6354     SetWindowText(hInput, buf);\r
6355     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6356   } else {\r
6357     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6358     SetWindowText(hInput, buf);\r
6359     sel.cpMin = 999999;\r
6360     sel.cpMax = 999999;\r
6361     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6362     SetFocus(hInput);\r
6363   }\r
6364 }\r
6365 \r
6366 LRESULT CALLBACK \r
6367 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6368 {\r
6369   HWND hInput;\r
6370   CHARRANGE sel;\r
6371 \r
6372   switch (message) {\r
6373   case WM_KEYDOWN:\r
6374     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6375     switch (wParam) {\r
6376     case VK_PRIOR:\r
6377       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6378       return 0;\r
6379     case VK_NEXT:\r
6380       sel.cpMin = 999999;\r
6381       sel.cpMax = 999999;\r
6382       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6383       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6384       return 0;\r
6385     }\r
6386     break;\r
6387   case WM_CHAR:\r
6388    if(wParam != '\022') {\r
6389     if (wParam == '\t') {\r
6390       if (GetKeyState(VK_SHIFT) < 0) {\r
6391         /* shifted */\r
6392         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6393         if (buttonDesc[0].hwnd) {\r
6394           SetFocus(buttonDesc[0].hwnd);\r
6395         } else {\r
6396           SetFocus(hwndMain);\r
6397         }\r
6398       } else {\r
6399         /* unshifted */\r
6400         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6401       }\r
6402     } else {\r
6403       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6404       JAWS_DELETE( SetFocus(hInput); )\r
6405       SendMessage(hInput, message, wParam, lParam);\r
6406     }\r
6407     return 0;\r
6408    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6409   case WM_RBUTTONUP:\r
6410     if (GetKeyState(VK_SHIFT) & ~1) {\r
6411       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6412         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6413     } else {\r
6414       POINT pt;\r
6415       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6416       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6417       if (sel.cpMin == sel.cpMax) {\r
6418         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6419         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6420       }\r
6421       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6422         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6423       }\r
6424       pt.x = LOWORD(lParam);\r
6425       pt.y = HIWORD(lParam);\r
6426       MenuPopup(hwnd, pt, hmenu, -1);\r
6427     }\r
6428     return 0;\r
6429   case WM_PASTE:\r
6430     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6431     SetFocus(hInput);\r
6432     return SendMessage(hInput, message, wParam, lParam);\r
6433   case WM_MBUTTONDOWN:\r
6434     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6435   case WM_RBUTTONDOWN:\r
6436     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6437       /* Move selection here if it was empty */\r
6438       POINT pt;\r
6439       pt.x = LOWORD(lParam);\r
6440       pt.y = HIWORD(lParam);\r
6441       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6442       if (sel.cpMin == sel.cpMax) {\r
6443         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6444         sel.cpMax = sel.cpMin;\r
6445         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6446       }\r
6447       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6448     }\r
6449     return 0;\r
6450   case WM_COMMAND:\r
6451     switch (LOWORD(wParam)) {\r
6452     case IDM_QuickPaste:\r
6453       {\r
6454         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6455         if (sel.cpMin == sel.cpMax) {\r
6456           MessageBeep(MB_ICONEXCLAMATION);\r
6457           return 0;\r
6458         }\r
6459         SendMessage(hwnd, WM_COPY, 0, 0);\r
6460         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6461         SendMessage(hInput, WM_PASTE, 0, 0);\r
6462         SetFocus(hInput);\r
6463         return 0;\r
6464       }\r
6465     case IDM_Cut:\r
6466       SendMessage(hwnd, WM_CUT, 0, 0);\r
6467       return 0;\r
6468     case IDM_Paste:\r
6469       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6470       return 0;\r
6471     case IDM_Copy:\r
6472       SendMessage(hwnd, WM_COPY, 0, 0);\r
6473       return 0;\r
6474     default:\r
6475       {\r
6476         int i = LOWORD(wParam) - IDM_CommandX;\r
6477         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6478             icsTextMenuEntry[i].command != NULL) {\r
6479           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6480                    icsTextMenuEntry[i].getname,\r
6481                    icsTextMenuEntry[i].immediate);\r
6482           return 0;\r
6483         }\r
6484       }\r
6485       break;\r
6486     }\r
6487     break;\r
6488   }\r
6489   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6490 }\r
6491 \r
6492 WNDPROC consoleInputWindowProc;\r
6493 \r
6494 LRESULT CALLBACK\r
6495 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6496 {\r
6497   char buf[MSG_SIZ];\r
6498   char *p;\r
6499   static BOOL sendNextChar = FALSE;\r
6500   static BOOL quoteNextChar = FALSE;\r
6501   InputSource *is = consoleInputSource;\r
6502   CHARFORMAT cf;\r
6503   CHARRANGE sel;\r
6504 \r
6505   switch (message) {\r
6506   case WM_CHAR:\r
6507     if (!appData.localLineEditing || sendNextChar) {\r
6508       is->buf[0] = (CHAR) wParam;\r
6509       is->count = 1;\r
6510       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6511       sendNextChar = FALSE;\r
6512       return 0;\r
6513     }\r
6514     if (quoteNextChar) {\r
6515       buf[0] = (char) wParam;\r
6516       buf[1] = NULLCHAR;\r
6517       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6518       quoteNextChar = FALSE;\r
6519       return 0;\r
6520     }\r
6521     switch (wParam) {\r
6522     case '\r':   /* Enter key */\r
6523       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6524       if (consoleEcho) SaveInHistory(is->buf);\r
6525       is->buf[is->count++] = '\n';\r
6526       is->buf[is->count] = NULLCHAR;\r
6527       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6528       if (consoleEcho) {\r
6529         ConsoleOutput(is->buf, is->count, TRUE);\r
6530       } else if (appData.localLineEditing) {\r
6531         ConsoleOutput("\n", 1, TRUE);\r
6532       }\r
6533       /* fall thru */\r
6534     case '\033': /* Escape key */\r
6535       SetWindowText(hwnd, "");\r
6536       cf.cbSize = sizeof(CHARFORMAT);\r
6537       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6538       if (consoleEcho) {\r
6539         cf.crTextColor = textAttribs[ColorNormal].color;\r
6540       } else {\r
6541         cf.crTextColor = COLOR_ECHOOFF;\r
6542       }\r
6543       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6544       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6545       return 0;\r
6546     case '\t':   /* Tab key */\r
6547       if (GetKeyState(VK_SHIFT) < 0) {\r
6548         /* shifted */\r
6549         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6550       } else {\r
6551         /* unshifted */\r
6552         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6553         if (buttonDesc[0].hwnd) {\r
6554           SetFocus(buttonDesc[0].hwnd);\r
6555         } else {\r
6556           SetFocus(hwndMain);\r
6557         }\r
6558       }\r
6559       return 0;\r
6560     case '\023': /* Ctrl+S */\r
6561       sendNextChar = TRUE;\r
6562       return 0;\r
6563     case '\021': /* Ctrl+Q */\r
6564       quoteNextChar = TRUE;\r
6565       return 0;\r
6566     JAWS_REPLAY\r
6567     default:\r
6568       break;\r
6569     }\r
6570     break;\r
6571   case WM_KEYDOWN:\r
6572     switch (wParam) {\r
6573     case VK_UP:\r
6574       GetWindowText(hwnd, buf, MSG_SIZ);\r
6575       p = PrevInHistory(buf);\r
6576       if (p != NULL) {\r
6577         SetWindowText(hwnd, p);\r
6578         sel.cpMin = 999999;\r
6579         sel.cpMax = 999999;\r
6580         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6581         return 0;\r
6582       }\r
6583       break;\r
6584     case VK_DOWN:\r
6585       p = NextInHistory();\r
6586       if (p != NULL) {\r
6587         SetWindowText(hwnd, p);\r
6588         sel.cpMin = 999999;\r
6589         sel.cpMax = 999999;\r
6590         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6591         return 0;\r
6592       }\r
6593       break;\r
6594     case VK_HOME:\r
6595     case VK_END:\r
6596       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6597       /* fall thru */\r
6598     case VK_PRIOR:\r
6599     case VK_NEXT:\r
6600       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6601       return 0;\r
6602     }\r
6603     break;\r
6604   case WM_MBUTTONDOWN:\r
6605     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6606       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6607     break;\r
6608   case WM_RBUTTONUP:\r
6609     if (GetKeyState(VK_SHIFT) & ~1) {\r
6610       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6611         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6612     } else {\r
6613       POINT pt;\r
6614       HMENU hmenu;\r
6615       hmenu = LoadMenu(hInst, "InputMenu");\r
6616       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6617       if (sel.cpMin == sel.cpMax) {\r
6618         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6619         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6620       }\r
6621       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6622         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6623       }\r
6624       pt.x = LOWORD(lParam);\r
6625       pt.y = HIWORD(lParam);\r
6626       MenuPopup(hwnd, pt, hmenu, -1);\r
6627     }\r
6628     return 0;\r
6629   case WM_COMMAND:\r
6630     switch (LOWORD(wParam)) { \r
6631     case IDM_Undo:\r
6632       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6633       return 0;\r
6634     case IDM_SelectAll:\r
6635       sel.cpMin = 0;\r
6636       sel.cpMax = -1; /*999999?*/\r
6637       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6638       return 0;\r
6639     case IDM_Cut:\r
6640       SendMessage(hwnd, WM_CUT, 0, 0);\r
6641       return 0;\r
6642     case IDM_Paste:\r
6643       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6644       return 0;\r
6645     case IDM_Copy:\r
6646       SendMessage(hwnd, WM_COPY, 0, 0);\r
6647       return 0;\r
6648     }\r
6649     break;\r
6650   }\r
6651   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6652 }\r
6653 \r
6654 #define CO_MAX  100000\r
6655 #define CO_TRIM   1000\r
6656 \r
6657 LRESULT CALLBACK\r
6658 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6659 {\r
6660   static SnapData sd;\r
6661   HWND hText, hInput;\r
6662   RECT rect;\r
6663   static int sizeX, sizeY;\r
6664   int newSizeX, newSizeY;\r
6665   MINMAXINFO *mmi;\r
6666   WORD wMask;\r
6667 \r
6668   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6669   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6670 \r
6671   switch (message) {\r
6672   case WM_NOTIFY:\r
6673     if (((NMHDR*)lParam)->code == EN_LINK)\r
6674     {\r
6675       ENLINK *pLink = (ENLINK*)lParam;\r
6676       if (pLink->msg == WM_LBUTTONUP)\r
6677       {\r
6678         TEXTRANGE tr;\r
6679 \r
6680         tr.chrg = pLink->chrg;\r
6681         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
6682         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
6683         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
6684         free(tr.lpstrText);\r
6685       }\r
6686     }\r
6687     break;\r
6688   case WM_INITDIALOG: /* message: initialize dialog box */\r
6689     hwndConsole = hDlg;\r
6690     SetFocus(hInput);\r
6691     consoleTextWindowProc = (WNDPROC)\r
6692       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6693     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6694     consoleInputWindowProc = (WNDPROC)\r
6695       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6696     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6697     Colorize(ColorNormal, TRUE);\r
6698     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6699     ChangedConsoleFont();\r
6700     GetClientRect(hDlg, &rect);\r
6701     sizeX = rect.right;\r
6702     sizeY = rect.bottom;\r
6703     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
6704         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
6705       WINDOWPLACEMENT wp;\r
6706       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6707       wp.length = sizeof(WINDOWPLACEMENT);\r
6708       wp.flags = 0;\r
6709       wp.showCmd = SW_SHOW;\r
6710       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6711       wp.rcNormalPosition.left = wpConsole.x;\r
6712       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6713       wp.rcNormalPosition.top = wpConsole.y;\r
6714       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6715       SetWindowPlacement(hDlg, &wp);\r
6716     }\r
6717 \r
6718    // [HGM] Chessknight's change 2004-07-13\r
6719    else { /* Determine Defaults */\r
6720        WINDOWPLACEMENT wp;\r
6721        wpConsole.x = wpMain.width + 1;\r
6722        wpConsole.y = wpMain.y;\r
6723        wpConsole.width = screenWidth -  wpMain.width;\r
6724        wpConsole.height = wpMain.height;\r
6725        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6726        wp.length = sizeof(WINDOWPLACEMENT);\r
6727        wp.flags = 0;\r
6728        wp.showCmd = SW_SHOW;\r
6729        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6730        wp.rcNormalPosition.left = wpConsole.x;\r
6731        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6732        wp.rcNormalPosition.top = wpConsole.y;\r
6733        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6734        SetWindowPlacement(hDlg, &wp);\r
6735     }\r
6736 \r
6737    // Allow hText to highlight URLs and send notifications on them\r
6738    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
6739    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
6740    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
6741    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
6742 \r
6743     return FALSE;\r
6744 \r
6745   case WM_SETFOCUS:\r
6746     SetFocus(hInput);\r
6747     return 0;\r
6748 \r
6749   case WM_CLOSE:\r
6750     ExitEvent(0);\r
6751     /* not reached */\r
6752     break;\r
6753 \r
6754   case WM_SIZE:\r
6755     if (IsIconic(hDlg)) break;\r
6756     newSizeX = LOWORD(lParam);\r
6757     newSizeY = HIWORD(lParam);\r
6758     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6759       RECT rectText, rectInput;\r
6760       POINT pt;\r
6761       int newTextHeight, newTextWidth;\r
6762       GetWindowRect(hText, &rectText);\r
6763       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6764       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6765       if (newTextHeight < 0) {\r
6766         newSizeY += -newTextHeight;\r
6767         newTextHeight = 0;\r
6768       }\r
6769       SetWindowPos(hText, NULL, 0, 0,\r
6770         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6771       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6772       pt.x = rectInput.left;\r
6773       pt.y = rectInput.top + newSizeY - sizeY;\r
6774       ScreenToClient(hDlg, &pt);\r
6775       SetWindowPos(hInput, NULL, \r
6776         pt.x, pt.y, /* needs client coords */   \r
6777         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6778         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6779     }\r
6780     sizeX = newSizeX;\r
6781     sizeY = newSizeY;\r
6782     break;\r
6783 \r
6784   case WM_GETMINMAXINFO:\r
6785     /* Prevent resizing window too small */\r
6786     mmi = (MINMAXINFO *) lParam;\r
6787     mmi->ptMinTrackSize.x = 100;\r
6788     mmi->ptMinTrackSize.y = 100;\r
6789     break;\r
6790 \r
6791   /* [AS] Snapping */\r
6792   case WM_ENTERSIZEMOVE:\r
6793     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
6794 \r
6795   case WM_SIZING:\r
6796     return OnSizing( &sd, hDlg, wParam, lParam );\r
6797 \r
6798   case WM_MOVING:\r
6799     return OnMoving( &sd, hDlg, wParam, lParam );\r
6800 \r
6801   case WM_EXITSIZEMOVE:\r
6802         UpdateICSWidth(hText);\r
6803     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
6804   }\r
6805 \r
6806   return DefWindowProc(hDlg, message, wParam, lParam);\r
6807 }\r
6808 \r
6809 \r
6810 VOID\r
6811 ConsoleCreate()\r
6812 {\r
6813   HWND hCons;\r
6814   if (hwndConsole) return;\r
6815   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6816   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6817 }\r
6818 \r
6819 \r
6820 VOID\r
6821 ConsoleOutput(char* data, int length, int forceVisible)\r
6822 {\r
6823   HWND hText;\r
6824   int trim, exlen;\r
6825   char *p, *q;\r
6826   char buf[CO_MAX+1];\r
6827   POINT pEnd;\r
6828   RECT rect;\r
6829   static int delayLF = 0;\r
6830   CHARRANGE savesel, sel;\r
6831 \r
6832   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6833   p = data;\r
6834   q = buf;\r
6835   if (delayLF) {\r
6836     *q++ = '\r';\r
6837     *q++ = '\n';\r
6838     delayLF = 0;\r
6839   }\r
6840   while (length--) {\r
6841     if (*p == '\n') {\r
6842       if (*++p) {\r
6843         *q++ = '\r';\r
6844         *q++ = '\n';\r
6845       } else {\r
6846         delayLF = 1;\r
6847       }\r
6848     } else if (*p == '\007') {\r
6849        MyPlaySound(&sounds[(int)SoundBell]);\r
6850        p++;\r
6851     } else {\r
6852       *q++ = *p++;\r
6853     }\r
6854   }\r
6855   *q = NULLCHAR;\r
6856   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
6857   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6858   /* Save current selection */\r
6859   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
6860   exlen = GetWindowTextLength(hText);\r
6861   /* Find out whether current end of text is visible */\r
6862   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
6863   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
6864   /* Trim existing text if it's too long */\r
6865   if (exlen + (q - buf) > CO_MAX) {\r
6866     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
6867     sel.cpMin = 0;\r
6868     sel.cpMax = trim;\r
6869     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6870     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
6871     exlen -= trim;\r
6872     savesel.cpMin -= trim;\r
6873     savesel.cpMax -= trim;\r
6874     if (exlen < 0) exlen = 0;\r
6875     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
6876     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
6877   }\r
6878   /* Append the new text */\r
6879   sel.cpMin = exlen;\r
6880   sel.cpMax = exlen;\r
6881   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6882   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
6883   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
6884   if (forceVisible || exlen == 0 ||\r
6885       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
6886        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
6887     /* Scroll to make new end of text visible if old end of text\r
6888        was visible or new text is an echo of user typein */\r
6889     sel.cpMin = 9999999;\r
6890     sel.cpMax = 9999999;\r
6891     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6892     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6893     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
6894     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6895   }\r
6896   if (savesel.cpMax == exlen || forceVisible) {\r
6897     /* Move insert point to new end of text if it was at the old\r
6898        end of text or if the new text is an echo of user typein */\r
6899     sel.cpMin = 9999999;\r
6900     sel.cpMax = 9999999;\r
6901     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6902   } else {\r
6903     /* Restore previous selection */\r
6904     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
6905   }\r
6906   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6907 }\r
6908 \r
6909 /*---------*/\r
6910 \r
6911 \r
6912 void\r
6913 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
6914 {\r
6915   char buf[100];\r
6916   char *str;\r
6917   COLORREF oldFg, oldBg;\r
6918   HFONT oldFont;\r
6919   RECT rect;\r
6920 \r
6921   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
6922 \r
6923   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6924   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6925   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6926 \r
6927   rect.left = x;\r
6928   rect.right = x + squareSize;\r
6929   rect.top  = y;\r
6930   rect.bottom = y + squareSize;\r
6931   str = buf;\r
6932 \r
6933   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
6934                     + (rightAlign ? (squareSize*2)/3 : 0),\r
6935              y, ETO_CLIPPED|ETO_OPAQUE,\r
6936              &rect, str, strlen(str), NULL);\r
6937 \r
6938   (void) SetTextColor(hdc, oldFg);\r
6939   (void) SetBkColor(hdc, oldBg);\r
6940   (void) SelectObject(hdc, oldFont);\r
6941 }\r
6942 \r
6943 void\r
6944 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
6945               RECT *rect, char *color, char *flagFell)\r
6946 {\r
6947   char buf[100];\r
6948   char *str;\r
6949   COLORREF oldFg, oldBg;\r
6950   HFONT oldFont;\r
6951 \r
6952   if (appData.clockMode) {\r
6953     if (tinyLayout)\r
6954       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
6955     else\r
6956       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
6957     str = buf;\r
6958   } else {\r
6959     str = color;\r
6960   }\r
6961 \r
6962   if (highlight) {\r
6963     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
6964     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
6965   } else {\r
6966     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
6967     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
6968   }\r
6969   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
6970 \r
6971   JAWS_SILENCE\r
6972 \r
6973   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6974              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
6975              rect, str, strlen(str), NULL);\r
6976   if(logoHeight > 0 && appData.clockMode) {\r
6977       RECT r;\r
6978       sprintf(buf, "%s %s", buf+7, flagFell);\r
6979       r.top = rect->top + logoHeight/2;\r
6980       r.left = rect->left;\r
6981       r.right = rect->right;\r
6982       r.bottom = rect->bottom;\r
6983       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
6984                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
6985                  &r, str, strlen(str), NULL);\r
6986   }\r
6987   (void) SetTextColor(hdc, oldFg);\r
6988   (void) SetBkColor(hdc, oldBg);\r
6989   (void) SelectObject(hdc, oldFont);\r
6990 }\r
6991 \r
6992 \r
6993 int\r
6994 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
6995            OVERLAPPED *ovl)\r
6996 {\r
6997   int ok, err;\r
6998 \r
6999   /* [AS]  */\r
7000   if( count <= 0 ) {\r
7001     if (appData.debugMode) {\r
7002       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7003     }\r
7004 \r
7005     return ERROR_INVALID_USER_BUFFER;\r
7006   }\r
7007 \r
7008   ResetEvent(ovl->hEvent);\r
7009   ovl->Offset = ovl->OffsetHigh = 0;\r
7010   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7011   if (ok) {\r
7012     err = NO_ERROR;\r
7013   } else {\r
7014     err = GetLastError();\r
7015     if (err == ERROR_IO_PENDING) {\r
7016       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7017       if (ok)\r
7018         err = NO_ERROR;\r
7019       else\r
7020         err = GetLastError();\r
7021     }\r
7022   }\r
7023   return err;\r
7024 }\r
7025 \r
7026 int\r
7027 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7028             OVERLAPPED *ovl)\r
7029 {\r
7030   int ok, err;\r
7031 \r
7032   ResetEvent(ovl->hEvent);\r
7033   ovl->Offset = ovl->OffsetHigh = 0;\r
7034   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7035   if (ok) {\r
7036     err = NO_ERROR;\r
7037   } else {\r
7038     err = GetLastError();\r
7039     if (err == ERROR_IO_PENDING) {\r
7040       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7041       if (ok)\r
7042         err = NO_ERROR;\r
7043       else\r
7044         err = GetLastError();\r
7045     }\r
7046   }\r
7047   return err;\r
7048 }\r
7049 \r
7050 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7051 void CheckForInputBufferFull( InputSource * is )\r
7052 {\r
7053     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7054         /* Look for end of line */\r
7055         char * p = is->buf;\r
7056         \r
7057         while( p < is->next && *p != '\n' ) {\r
7058             p++;\r
7059         }\r
7060 \r
7061         if( p >= is->next ) {\r
7062             if (appData.debugMode) {\r
7063                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7064             }\r
7065 \r
7066             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7067             is->count = (DWORD) -1;\r
7068             is->next = is->buf;\r
7069         }\r
7070     }\r
7071 }\r
7072 \r
7073 DWORD\r
7074 InputThread(LPVOID arg)\r
7075 {\r
7076   InputSource *is;\r
7077   OVERLAPPED ovl;\r
7078 \r
7079   is = (InputSource *) arg;\r
7080   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7081   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7082   while (is->hThread != NULL) {\r
7083     is->error = DoReadFile(is->hFile, is->next,\r
7084                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7085                            &is->count, &ovl);\r
7086     if (is->error == NO_ERROR) {\r
7087       is->next += is->count;\r
7088     } else {\r
7089       if (is->error == ERROR_BROKEN_PIPE) {\r
7090         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7091         is->count = 0;\r
7092       } else {\r
7093         is->count = (DWORD) -1;\r
7094         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7095         break; \r
7096       }\r
7097     }\r
7098 \r
7099     CheckForInputBufferFull( is );\r
7100 \r
7101     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7102 \r
7103     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7104 \r
7105     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7106   }\r
7107 \r
7108   CloseHandle(ovl.hEvent);\r
7109   CloseHandle(is->hFile);\r
7110 \r
7111   if (appData.debugMode) {\r
7112     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7113   }\r
7114 \r
7115   return 0;\r
7116 }\r
7117 \r
7118 \r
7119 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7120 DWORD\r
7121 NonOvlInputThread(LPVOID arg)\r
7122 {\r
7123   InputSource *is;\r
7124   char *p, *q;\r
7125   int i;\r
7126   char prev;\r
7127 \r
7128   is = (InputSource *) arg;\r
7129   while (is->hThread != NULL) {\r
7130     is->error = ReadFile(is->hFile, is->next,\r
7131                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7132                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7133     if (is->error == NO_ERROR) {\r
7134       /* Change CRLF to LF */\r
7135       if (is->next > is->buf) {\r
7136         p = is->next - 1;\r
7137         i = is->count + 1;\r
7138       } else {\r
7139         p = is->next;\r
7140         i = is->count;\r
7141       }\r
7142       q = p;\r
7143       prev = NULLCHAR;\r
7144       while (i > 0) {\r
7145         if (prev == '\r' && *p == '\n') {\r
7146           *(q-1) = '\n';\r
7147           is->count--;\r
7148         } else { \r
7149           *q++ = *p;\r
7150         }\r
7151         prev = *p++;\r
7152         i--;\r
7153       }\r
7154       *q = NULLCHAR;\r
7155       is->next = q;\r
7156     } else {\r
7157       if (is->error == ERROR_BROKEN_PIPE) {\r
7158         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7159         is->count = 0; \r
7160       } else {\r
7161         is->count = (DWORD) -1;\r
7162       }\r
7163     }\r
7164 \r
7165     CheckForInputBufferFull( is );\r
7166 \r
7167     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7168 \r
7169     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7170 \r
7171     if (is->count < 0) break;  /* Quit on error */\r
7172   }\r
7173   CloseHandle(is->hFile);\r
7174   return 0;\r
7175 }\r
7176 \r
7177 DWORD\r
7178 SocketInputThread(LPVOID arg)\r
7179 {\r
7180   InputSource *is;\r
7181 \r
7182   is = (InputSource *) arg;\r
7183   while (is->hThread != NULL) {\r
7184     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7185     if ((int)is->count == SOCKET_ERROR) {\r
7186       is->count = (DWORD) -1;\r
7187       is->error = WSAGetLastError();\r
7188     } else {\r
7189       is->error = NO_ERROR;\r
7190       is->next += is->count;\r
7191       if (is->count == 0 && is->second == is) {\r
7192         /* End of file on stderr; quit with no message */\r
7193         break;\r
7194       }\r
7195     }\r
7196     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7197 \r
7198     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7199 \r
7200     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7201   }\r
7202   return 0;\r
7203 }\r
7204 \r
7205 VOID\r
7206 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7207 {\r
7208   InputSource *is;\r
7209 \r
7210   is = (InputSource *) lParam;\r
7211   if (is->lineByLine) {\r
7212     /* Feed in lines one by one */\r
7213     char *p = is->buf;\r
7214     char *q = p;\r
7215     while (q < is->next) {\r
7216       if (*q++ == '\n') {\r
7217         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7218         p = q;\r
7219       }\r
7220     }\r
7221     \r
7222     /* Move any partial line to the start of the buffer */\r
7223     q = is->buf;\r
7224     while (p < is->next) {\r
7225       *q++ = *p++;\r
7226     }\r
7227     is->next = q;\r
7228 \r
7229     if (is->error != NO_ERROR || is->count == 0) {\r
7230       /* Notify backend of the error.  Note: If there was a partial\r
7231          line at the end, it is not flushed through. */\r
7232       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7233     }\r
7234   } else {\r
7235     /* Feed in the whole chunk of input at once */\r
7236     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7237     is->next = is->buf;\r
7238   }\r
7239 }\r
7240 \r
7241 /*---------------------------------------------------------------------------*\\r
7242  *\r
7243  *  Menu enables. Used when setting various modes.\r
7244  *\r
7245 \*---------------------------------------------------------------------------*/\r
7246 \r
7247 typedef struct {\r
7248   int item;\r
7249   int flags;\r
7250 } Enables;\r
7251 \r
7252 VOID\r
7253 GreyRevert(Boolean grey)\r
7254 { // [HGM] vari: for retracting variations in local mode\r
7255   HMENU hmenu = GetMenu(hwndMain);\r
7256   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7257 }\r
7258 \r
7259 VOID\r
7260 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7261 {\r
7262   while (enab->item > 0) {\r
7263     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7264     enab++;\r
7265   }\r
7266 }\r
7267 \r
7268 Enables gnuEnables[] = {\r
7269   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7270   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7271   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7272   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7273   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7274   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7275   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7276   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7277   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7278   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7279   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7280   { -1, -1 }\r
7281 };\r
7282 \r
7283 Enables icsEnables[] = {\r
7284   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7285   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7286   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7287   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7288   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7289   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7290   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7291   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7292   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7293   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7294   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7295   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7296   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7297   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7298   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7299   { -1, -1 }\r
7300 };\r
7301 \r
7302 #ifdef ZIPPY\r
7303 Enables zippyEnables[] = {\r
7304   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7305   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7306   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7307   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7308   { -1, -1 }\r
7309 };\r
7310 #endif\r
7311 \r
7312 Enables ncpEnables[] = {\r
7313   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7314   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7315   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7316   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7317   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7318   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7319   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7320   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7321   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7322   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7323   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7324   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7325   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7326   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7327   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7328   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7329   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7330   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7331   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7332   { -1, -1 }\r
7333 };\r
7334 \r
7335 Enables trainingOnEnables[] = {\r
7336   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7337   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7338   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7339   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7340   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7341   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7342   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7343   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7344   { -1, -1 }\r
7345 };\r
7346 \r
7347 Enables trainingOffEnables[] = {\r
7348   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7349   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7350   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7351   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7352   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7353   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7354   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7355   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7356   { -1, -1 }\r
7357 };\r
7358 \r
7359 /* These modify either ncpEnables or gnuEnables */\r
7360 Enables cmailEnables[] = {\r
7361   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7362   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7363   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7364   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7365   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7366   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7367   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7368   { -1, -1 }\r
7369 };\r
7370 \r
7371 Enables machineThinkingEnables[] = {\r
7372   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7373   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7374   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7375   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7376   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7377   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7378   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7379   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7380   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7381   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7382   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7383   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7384   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7385   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7386   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7387   { -1, -1 }\r
7388 };\r
7389 \r
7390 Enables userThinkingEnables[] = {\r
7391   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7392   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7393   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7394   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7395   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7396   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7397   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7398   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7399   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7400   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7401   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7402   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7403   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7404   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7405   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7406   { -1, -1 }\r
7407 };\r
7408 \r
7409 /*---------------------------------------------------------------------------*\\r
7410  *\r
7411  *  Front-end interface functions exported by XBoard.\r
7412  *  Functions appear in same order as prototypes in frontend.h.\r
7413  * \r
7414 \*---------------------------------------------------------------------------*/\r
7415 VOID\r
7416 ModeHighlight()\r
7417 {\r
7418   static UINT prevChecked = 0;\r
7419   static int prevPausing = 0;\r
7420   UINT nowChecked;\r
7421 \r
7422   if (pausing != prevPausing) {\r
7423     prevPausing = pausing;\r
7424     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7425                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7426     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7427   }\r
7428 \r
7429   switch (gameMode) {\r
7430   case BeginningOfGame:\r
7431     if (appData.icsActive)\r
7432       nowChecked = IDM_IcsClient;\r
7433     else if (appData.noChessProgram)\r
7434       nowChecked = IDM_EditGame;\r
7435     else\r
7436       nowChecked = IDM_MachineBlack;\r
7437     break;\r
7438   case MachinePlaysBlack:\r
7439     nowChecked = IDM_MachineBlack;\r
7440     break;\r
7441   case MachinePlaysWhite:\r
7442     nowChecked = IDM_MachineWhite;\r
7443     break;\r
7444   case TwoMachinesPlay:\r
7445     nowChecked = IDM_TwoMachines;\r
7446     break;\r
7447   case AnalyzeMode:\r
7448     nowChecked = IDM_AnalysisMode;\r
7449     break;\r
7450   case AnalyzeFile:\r
7451     nowChecked = IDM_AnalyzeFile;\r
7452     break;\r
7453   case EditGame:\r
7454     nowChecked = IDM_EditGame;\r
7455     break;\r
7456   case PlayFromGameFile:\r
7457     nowChecked = IDM_LoadGame;\r
7458     break;\r
7459   case EditPosition:\r
7460     nowChecked = IDM_EditPosition;\r
7461     break;\r
7462   case Training:\r
7463     nowChecked = IDM_Training;\r
7464     break;\r
7465   case IcsPlayingWhite:\r
7466   case IcsPlayingBlack:\r
7467   case IcsObserving:\r
7468   case IcsIdle:\r
7469     nowChecked = IDM_IcsClient;\r
7470     break;\r
7471   default:\r
7472   case EndOfGame:\r
7473     nowChecked = 0;\r
7474     break;\r
7475   }\r
7476   if (prevChecked != 0)\r
7477     (void) CheckMenuItem(GetMenu(hwndMain),\r
7478                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7479   if (nowChecked != 0)\r
7480     (void) CheckMenuItem(GetMenu(hwndMain),\r
7481                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7482 \r
7483   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7484     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7485                           MF_BYCOMMAND|MF_ENABLED);\r
7486   } else {\r
7487     (void) EnableMenuItem(GetMenu(hwndMain), \r
7488                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7489   }\r
7490 \r
7491   prevChecked = nowChecked;\r
7492 \r
7493   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7494   if (appData.icsActive) {\r
7495        if (appData.icsEngineAnalyze) {\r
7496                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7497                        MF_BYCOMMAND|MF_CHECKED);\r
7498        } else {\r
7499                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7500                        MF_BYCOMMAND|MF_UNCHECKED);\r
7501        }\r
7502   }\r
7503 }\r
7504 \r
7505 VOID\r
7506 SetICSMode()\r
7507 {\r
7508   HMENU hmenu = GetMenu(hwndMain);\r
7509   SetMenuEnables(hmenu, icsEnables);\r
7510   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7511     MF_BYPOSITION|MF_ENABLED);\r
7512 #ifdef ZIPPY\r
7513   if (appData.zippyPlay) {\r
7514     SetMenuEnables(hmenu, zippyEnables);\r
7515     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7516          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7517           MF_BYCOMMAND|MF_ENABLED);\r
7518   }\r
7519 #endif\r
7520 }\r
7521 \r
7522 VOID\r
7523 SetGNUMode()\r
7524 {\r
7525   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7526 }\r
7527 \r
7528 VOID\r
7529 SetNCPMode()\r
7530 {\r
7531   HMENU hmenu = GetMenu(hwndMain);\r
7532   SetMenuEnables(hmenu, ncpEnables);\r
7533   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7534     MF_BYPOSITION|MF_GRAYED);\r
7535     DrawMenuBar(hwndMain);\r
7536 }\r
7537 \r
7538 VOID\r
7539 SetCmailMode()\r
7540 {\r
7541   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7542 }\r
7543 \r
7544 VOID \r
7545 SetTrainingModeOn()\r
7546 {\r
7547   int i;\r
7548   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7549   for (i = 0; i < N_BUTTONS; i++) {\r
7550     if (buttonDesc[i].hwnd != NULL)\r
7551       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7552   }\r
7553   CommentPopDown();\r
7554 }\r
7555 \r
7556 VOID SetTrainingModeOff()\r
7557 {\r
7558   int i;\r
7559   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7560   for (i = 0; i < N_BUTTONS; i++) {\r
7561     if (buttonDesc[i].hwnd != NULL)\r
7562       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7563   }\r
7564 }\r
7565 \r
7566 \r
7567 VOID\r
7568 SetUserThinkingEnables()\r
7569 {\r
7570   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7571 }\r
7572 \r
7573 VOID\r
7574 SetMachineThinkingEnables()\r
7575 {\r
7576   HMENU hMenu = GetMenu(hwndMain);\r
7577   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7578 \r
7579   SetMenuEnables(hMenu, machineThinkingEnables);\r
7580 \r
7581   if (gameMode == MachinePlaysBlack) {\r
7582     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7583   } else if (gameMode == MachinePlaysWhite) {\r
7584     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7585   } else if (gameMode == TwoMachinesPlay) {\r
7586     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7587   }\r
7588 }\r
7589 \r
7590 \r
7591 VOID\r
7592 DisplayTitle(char *str)\r
7593 {\r
7594   char title[MSG_SIZ], *host;\r
7595   if (str[0] != NULLCHAR) {\r
7596     strcpy(title, str);\r
7597   } else if (appData.icsActive) {\r
7598     if (appData.icsCommPort[0] != NULLCHAR)\r
7599       host = "ICS";\r
7600     else \r
7601       host = appData.icsHost;\r
7602     sprintf(title, "%s: %s", szTitle, host);\r
7603   } else if (appData.noChessProgram) {\r
7604     strcpy(title, szTitle);\r
7605   } else {\r
7606     strcpy(title, szTitle);\r
7607     strcat(title, ": ");\r
7608     strcat(title, first.tidy);\r
7609   }\r
7610   SetWindowText(hwndMain, title);\r
7611 }\r
7612 \r
7613 \r
7614 VOID\r
7615 DisplayMessage(char *str1, char *str2)\r
7616 {\r
7617   HDC hdc;\r
7618   HFONT oldFont;\r
7619   int remain = MESSAGE_TEXT_MAX - 1;\r
7620   int len;\r
7621 \r
7622   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7623   messageText[0] = NULLCHAR;\r
7624   if (*str1) {\r
7625     len = strlen(str1);\r
7626     if (len > remain) len = remain;\r
7627     strncpy(messageText, str1, len);\r
7628     messageText[len] = NULLCHAR;\r
7629     remain -= len;\r
7630   }\r
7631   if (*str2 && remain >= 2) {\r
7632     if (*str1) {\r
7633       strcat(messageText, "  ");\r
7634       remain -= 2;\r
7635     }\r
7636     len = strlen(str2);\r
7637     if (len > remain) len = remain;\r
7638     strncat(messageText, str2, len);\r
7639   }\r
7640   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7641 \r
7642   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
7643 \r
7644   SAYMACHINEMOVE();\r
7645 \r
7646   hdc = GetDC(hwndMain);\r
7647   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7648   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7649              &messageRect, messageText, strlen(messageText), NULL);\r
7650   (void) SelectObject(hdc, oldFont);\r
7651   (void) ReleaseDC(hwndMain, hdc);\r
7652 }\r
7653 \r
7654 VOID\r
7655 DisplayError(char *str, int error)\r
7656 {\r
7657   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7658   int len;\r
7659 \r
7660   if (error == 0) {\r
7661     strcpy(buf, str);\r
7662   } else {\r
7663     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7664                         NULL, error, LANG_NEUTRAL,\r
7665                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7666     if (len > 0) {\r
7667       sprintf(buf, "%s:\n%s", str, buf2);\r
7668     } else {\r
7669       ErrorMap *em = errmap;\r
7670       while (em->err != 0 && em->err != error) em++;\r
7671       if (em->err != 0) {\r
7672         sprintf(buf, "%s:\n%s", str, em->msg);\r
7673       } else {\r
7674         sprintf(buf, "%s:\nError code %d", str, error);\r
7675       }\r
7676     }\r
7677   }\r
7678   \r
7679   ErrorPopUp("Error", buf);\r
7680 }\r
7681 \r
7682 \r
7683 VOID\r
7684 DisplayMoveError(char *str)\r
7685 {\r
7686   fromX = fromY = -1;\r
7687   ClearHighlights();\r
7688   DrawPosition(FALSE, NULL);\r
7689   if (appData.popupMoveErrors) {\r
7690     ErrorPopUp("Error", str);\r
7691   } else {\r
7692     DisplayMessage(str, "");\r
7693     moveErrorMessageUp = TRUE;\r
7694   }\r
7695 }\r
7696 \r
7697 VOID\r
7698 DisplayFatalError(char *str, int error, int exitStatus)\r
7699 {\r
7700   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7701   int len;\r
7702   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7703 \r
7704   if (error != 0) {\r
7705     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7706                         NULL, error, LANG_NEUTRAL,\r
7707                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7708     if (len > 0) {\r
7709       sprintf(buf, "%s:\n%s", str, buf2);\r
7710     } else {\r
7711       ErrorMap *em = errmap;\r
7712       while (em->err != 0 && em->err != error) em++;\r
7713       if (em->err != 0) {\r
7714         sprintf(buf, "%s:\n%s", str, em->msg);\r
7715       } else {\r
7716         sprintf(buf, "%s:\nError code %d", str, error);\r
7717       }\r
7718     }\r
7719     str = buf;\r
7720   }\r
7721   if (appData.debugMode) {\r
7722     fprintf(debugFP, "%s: %s\n", label, str);\r
7723   }\r
7724   if (appData.popupExitMessage) {\r
7725     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7726                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7727   }\r
7728   ExitEvent(exitStatus);\r
7729 }\r
7730 \r
7731 \r
7732 VOID\r
7733 DisplayInformation(char *str)\r
7734 {\r
7735   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7736 }\r
7737 \r
7738 \r
7739 VOID\r
7740 DisplayNote(char *str)\r
7741 {\r
7742   ErrorPopUp("Note", str);\r
7743 }\r
7744 \r
7745 \r
7746 typedef struct {\r
7747   char *title, *question, *replyPrefix;\r
7748   ProcRef pr;\r
7749 } QuestionParams;\r
7750 \r
7751 LRESULT CALLBACK\r
7752 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7753 {\r
7754   static QuestionParams *qp;\r
7755   char reply[MSG_SIZ];\r
7756   int len, err;\r
7757 \r
7758   switch (message) {\r
7759   case WM_INITDIALOG:\r
7760     qp = (QuestionParams *) lParam;\r
7761     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7762     SetWindowText(hDlg, qp->title);\r
7763     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7764     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7765     return FALSE;\r
7766 \r
7767   case WM_COMMAND:\r
7768     switch (LOWORD(wParam)) {\r
7769     case IDOK:\r
7770       strcpy(reply, qp->replyPrefix);\r
7771       if (*reply) strcat(reply, " ");\r
7772       len = strlen(reply);\r
7773       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7774       strcat(reply, "\n");\r
7775       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7776       EndDialog(hDlg, TRUE);\r
7777       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7778       return TRUE;\r
7779     case IDCANCEL:\r
7780       EndDialog(hDlg, FALSE);\r
7781       return TRUE;\r
7782     default:\r
7783       break;\r
7784     }\r
7785     break;\r
7786   }\r
7787   return FALSE;\r
7788 }\r
7789 \r
7790 VOID\r
7791 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7792 {\r
7793     QuestionParams qp;\r
7794     FARPROC lpProc;\r
7795     \r
7796     qp.title = title;\r
7797     qp.question = question;\r
7798     qp.replyPrefix = replyPrefix;\r
7799     qp.pr = pr;\r
7800     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7801     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7802       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7803     FreeProcInstance(lpProc);\r
7804 }\r
7805 \r
7806 /* [AS] Pick FRC position */\r
7807 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7808 {\r
7809     static int * lpIndexFRC;\r
7810     BOOL index_is_ok;\r
7811     char buf[16];\r
7812 \r
7813     switch( message )\r
7814     {\r
7815     case WM_INITDIALOG:\r
7816         lpIndexFRC = (int *) lParam;\r
7817 \r
7818         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7819 \r
7820         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
7821         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
7822         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
7823         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
7824 \r
7825         break;\r
7826 \r
7827     case WM_COMMAND:\r
7828         switch( LOWORD(wParam) ) {\r
7829         case IDOK:\r
7830             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7831             EndDialog( hDlg, 0 );\r
7832             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
7833             return TRUE;\r
7834         case IDCANCEL:\r
7835             EndDialog( hDlg, 1 );   \r
7836             return TRUE;\r
7837         case IDC_NFG_Edit:\r
7838             if( HIWORD(wParam) == EN_CHANGE ) {\r
7839                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7840 \r
7841                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
7842             }\r
7843             return TRUE;\r
7844         case IDC_NFG_Random:\r
7845             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
7846             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
7847             return TRUE;\r
7848         }\r
7849 \r
7850         break;\r
7851     }\r
7852 \r
7853     return FALSE;\r
7854 }\r
7855 \r
7856 int NewGameFRC()\r
7857 {\r
7858     int result;\r
7859     int index = appData.defaultFrcPosition;\r
7860     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
7861 \r
7862     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
7863 \r
7864     if( result == 0 ) {\r
7865         appData.defaultFrcPosition = index;\r
7866     }\r
7867 \r
7868     return result;\r
7869 }\r
7870 \r
7871 /* [AS] Game list options */\r
7872 typedef struct {\r
7873     char id;\r
7874     char * name;\r
7875 } GLT_Item;\r
7876 \r
7877 static GLT_Item GLT_ItemInfo[] = {\r
7878     { GLT_EVENT,      "Event" },\r
7879     { GLT_SITE,       "Site" },\r
7880     { GLT_DATE,       "Date" },\r
7881     { GLT_ROUND,      "Round" },\r
7882     { GLT_PLAYERS,    "Players" },\r
7883     { GLT_RESULT,     "Result" },\r
7884     { GLT_WHITE_ELO,  "White Rating" },\r
7885     { GLT_BLACK_ELO,  "Black Rating" },\r
7886     { GLT_TIME_CONTROL,"Time Control" },\r
7887     { GLT_VARIANT,    "Variant" },\r
7888     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
7889     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
7890     { 0, 0 }\r
7891 };\r
7892 \r
7893 const char * GLT_FindItem( char id )\r
7894 {\r
7895     const char * result = 0;\r
7896 \r
7897     GLT_Item * list = GLT_ItemInfo;\r
7898 \r
7899     while( list->id != 0 ) {\r
7900         if( list->id == id ) {\r
7901             result = list->name;\r
7902             break;\r
7903         }\r
7904 \r
7905         list++;\r
7906     }\r
7907 \r
7908     return result;\r
7909 }\r
7910 \r
7911 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
7912 {\r
7913     const char * name = GLT_FindItem( id );\r
7914 \r
7915     if( name != 0 ) {\r
7916         if( index >= 0 ) {\r
7917             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
7918         }\r
7919         else {\r
7920             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
7921         }\r
7922     }\r
7923 }\r
7924 \r
7925 void GLT_TagsToList( HWND hDlg, char * tags )\r
7926 {\r
7927     char * pc = tags;\r
7928 \r
7929     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
7930 \r
7931     while( *pc ) {\r
7932         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
7933         pc++;\r
7934     }\r
7935 \r
7936     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
7937 \r
7938     pc = GLT_ALL_TAGS;\r
7939 \r
7940     while( *pc ) {\r
7941         if( strchr( tags, *pc ) == 0 ) {\r
7942             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
7943         }\r
7944         pc++;\r
7945     }\r
7946 \r
7947     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
7948 }\r
7949 \r
7950 char GLT_ListItemToTag( HWND hDlg, int index )\r
7951 {\r
7952     char result = '\0';\r
7953     char name[128];\r
7954 \r
7955     GLT_Item * list = GLT_ItemInfo;\r
7956 \r
7957     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
7958         while( list->id != 0 ) {\r
7959             if( strcmp( list->name, name ) == 0 ) {\r
7960                 result = list->id;\r
7961                 break;\r
7962             }\r
7963 \r
7964             list++;\r
7965         }\r
7966     }\r
7967 \r
7968     return result;\r
7969 }\r
7970 \r
7971 void GLT_MoveSelection( HWND hDlg, int delta )\r
7972 {\r
7973     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
7974     int idx2 = idx1 + delta;\r
7975     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
7976 \r
7977     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
7978         char buf[128];\r
7979 \r
7980         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
7981         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
7982         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
7983         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
7984     }\r
7985 }\r
7986 \r
7987 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7988 {\r
7989     static char glt[64];\r
7990     static char * lpUserGLT;\r
7991 \r
7992     switch( message )\r
7993     {\r
7994     case WM_INITDIALOG:\r
7995         lpUserGLT = (char *) lParam;\r
7996         \r
7997         strcpy( glt, lpUserGLT );\r
7998 \r
7999         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8000 \r
8001         /* Initialize list */\r
8002         GLT_TagsToList( hDlg, glt );\r
8003 \r
8004         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8005 \r
8006         break;\r
8007 \r
8008     case WM_COMMAND:\r
8009         switch( LOWORD(wParam) ) {\r
8010         case IDOK:\r
8011             {\r
8012                 char * pc = lpUserGLT;\r
8013                 int idx = 0;\r
8014 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8015                 char id;\r
8016 \r
8017                 do {\r
8018                     id = GLT_ListItemToTag( hDlg, idx );\r
8019 \r
8020                     *pc++ = id;\r
8021                     idx++;\r
8022                 } while( id != '\0' );\r
8023             }\r
8024             EndDialog( hDlg, 0 );\r
8025             return TRUE;\r
8026         case IDCANCEL:\r
8027             EndDialog( hDlg, 1 );\r
8028             return TRUE;\r
8029 \r
8030         case IDC_GLT_Default:\r
8031             strcpy( glt, GLT_DEFAULT_TAGS );\r
8032             GLT_TagsToList( hDlg, glt );\r
8033             return TRUE;\r
8034 \r
8035         case IDC_GLT_Restore:\r
8036             strcpy( glt, lpUserGLT );\r
8037             GLT_TagsToList( hDlg, glt );\r
8038             return TRUE;\r
8039 \r
8040         case IDC_GLT_Up:\r
8041             GLT_MoveSelection( hDlg, -1 );\r
8042             return TRUE;\r
8043 \r
8044         case IDC_GLT_Down:\r
8045             GLT_MoveSelection( hDlg, +1 );\r
8046             return TRUE;\r
8047         }\r
8048 \r
8049         break;\r
8050     }\r
8051 \r
8052     return FALSE;\r
8053 }\r
8054 \r
8055 int GameListOptions()\r
8056 {\r
8057     char glt[64];\r
8058     int result;\r
8059     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8060 \r
8061     strcpy( glt, appData.gameListTags );\r
8062 \r
8063     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8064 \r
8065     if( result == 0 ) {\r
8066         /* [AS] Memory leak here! */\r
8067         appData.gameListTags = strdup( glt ); \r
8068     }\r
8069 \r
8070     return result;\r
8071 }\r
8072 \r
8073 \r
8074 VOID\r
8075 DisplayIcsInteractionTitle(char *str)\r
8076 {\r
8077   char consoleTitle[MSG_SIZ];\r
8078 \r
8079   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8080   SetWindowText(hwndConsole, consoleTitle);\r
8081 }\r
8082 \r
8083 void\r
8084 DrawPosition(int fullRedraw, Board board)\r
8085 {\r
8086   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8087 }\r
8088 \r
8089 void NotifyFrontendLogin()\r
8090 {\r
8091         if (hwndConsole)\r
8092                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8093 }\r
8094 \r
8095 VOID\r
8096 ResetFrontEnd()\r
8097 {\r
8098   fromX = fromY = -1;\r
8099   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8100     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8101     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8102     dragInfo.lastpos = dragInfo.pos;\r
8103     dragInfo.start.x = dragInfo.start.y = -1;\r
8104     dragInfo.from = dragInfo.start;\r
8105     ReleaseCapture();\r
8106     DrawPosition(TRUE, NULL);\r
8107   }\r
8108 }\r
8109 \r
8110 \r
8111 VOID\r
8112 CommentPopUp(char *title, char *str)\r
8113 {\r
8114   HWND hwnd = GetActiveWindow();\r
8115   EitherCommentPopUp(0, title, str, FALSE);\r
8116   SAY(str);\r
8117   SetActiveWindow(hwnd);\r
8118 }\r
8119 \r
8120 VOID\r
8121 CommentPopDown(void)\r
8122 {\r
8123   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8124   if (commentDialog) {\r
8125     ShowWindow(commentDialog, SW_HIDE);\r
8126   }\r
8127   commentUp = FALSE;\r
8128 }\r
8129 \r
8130 VOID\r
8131 EditCommentPopUp(int index, char *title, char *str)\r
8132 {\r
8133   EitherCommentPopUp(index, title, str, TRUE);\r
8134 }\r
8135 \r
8136 \r
8137 VOID\r
8138 RingBell()\r
8139 {\r
8140   MyPlaySound(&sounds[(int)SoundMove]);\r
8141 }\r
8142 \r
8143 VOID PlayIcsWinSound()\r
8144 {\r
8145   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8146 }\r
8147 \r
8148 VOID PlayIcsLossSound()\r
8149 {\r
8150   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8151 }\r
8152 \r
8153 VOID PlayIcsDrawSound()\r
8154 {\r
8155   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8156 }\r
8157 \r
8158 VOID PlayIcsUnfinishedSound()\r
8159 {\r
8160   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8161 }\r
8162 \r
8163 VOID\r
8164 PlayAlarmSound()\r
8165 {\r
8166   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8167 }\r
8168 \r
8169 \r
8170 VOID\r
8171 EchoOn()\r
8172 {\r
8173   HWND hInput;\r
8174   consoleEcho = TRUE;\r
8175   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8176   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8177   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8178 }\r
8179 \r
8180 \r
8181 VOID\r
8182 EchoOff()\r
8183 {\r
8184   CHARFORMAT cf;\r
8185   HWND hInput;\r
8186   consoleEcho = FALSE;\r
8187   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8188   /* This works OK: set text and background both to the same color */\r
8189   cf = consoleCF;\r
8190   cf.crTextColor = COLOR_ECHOOFF;\r
8191   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8192   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8193 }\r
8194 \r
8195 /* No Raw()...? */\r
8196 \r
8197 void Colorize(ColorClass cc, int continuation)\r
8198 {\r
8199   currentColorClass = cc;\r
8200   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8201   consoleCF.crTextColor = textAttribs[cc].color;\r
8202   consoleCF.dwEffects = textAttribs[cc].effects;\r
8203   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8204 }\r
8205 \r
8206 char *\r
8207 UserName()\r
8208 {\r
8209   static char buf[MSG_SIZ];\r
8210   DWORD bufsiz = MSG_SIZ;\r
8211 \r
8212   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8213         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8214   }\r
8215   if (!GetUserName(buf, &bufsiz)) {\r
8216     /*DisplayError("Error getting user name", GetLastError());*/\r
8217     strcpy(buf, "User");\r
8218   }\r
8219   return buf;\r
8220 }\r
8221 \r
8222 char *\r
8223 HostName()\r
8224 {\r
8225   static char buf[MSG_SIZ];\r
8226   DWORD bufsiz = MSG_SIZ;\r
8227 \r
8228   if (!GetComputerName(buf, &bufsiz)) {\r
8229     /*DisplayError("Error getting host name", GetLastError());*/\r
8230     strcpy(buf, "Unknown");\r
8231   }\r
8232   return buf;\r
8233 }\r
8234 \r
8235 \r
8236 int\r
8237 ClockTimerRunning()\r
8238 {\r
8239   return clockTimerEvent != 0;\r
8240 }\r
8241 \r
8242 int\r
8243 StopClockTimer()\r
8244 {\r
8245   if (clockTimerEvent == 0) return FALSE;\r
8246   KillTimer(hwndMain, clockTimerEvent);\r
8247   clockTimerEvent = 0;\r
8248   return TRUE;\r
8249 }\r
8250 \r
8251 void\r
8252 StartClockTimer(long millisec)\r
8253 {\r
8254   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8255                              (UINT) millisec, NULL);\r
8256 }\r
8257 \r
8258 void\r
8259 DisplayWhiteClock(long timeRemaining, int highlight)\r
8260 {\r
8261   HDC hdc;\r
8262   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8263 \r
8264   if(appData.noGUI) return;\r
8265   hdc = GetDC(hwndMain);\r
8266   if (!IsIconic(hwndMain)) {\r
8267     DisplayAClock(hdc, timeRemaining, highlight, \r
8268                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
8269   }\r
8270   if (highlight && iconCurrent == iconBlack) {\r
8271     iconCurrent = iconWhite;\r
8272     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8273     if (IsIconic(hwndMain)) {\r
8274       DrawIcon(hdc, 2, 2, iconCurrent);\r
8275     }\r
8276   }\r
8277   (void) ReleaseDC(hwndMain, hdc);\r
8278   if (hwndConsole)\r
8279     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8280 }\r
8281 \r
8282 void\r
8283 DisplayBlackClock(long timeRemaining, int highlight)\r
8284 {\r
8285   HDC hdc;\r
8286   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8287 \r
8288   if(appData.noGUI) return;\r
8289   hdc = GetDC(hwndMain);\r
8290   if (!IsIconic(hwndMain)) {\r
8291     DisplayAClock(hdc, timeRemaining, highlight, \r
8292                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8293   }\r
8294   if (highlight && iconCurrent == iconWhite) {\r
8295     iconCurrent = iconBlack;\r
8296     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8297     if (IsIconic(hwndMain)) {\r
8298       DrawIcon(hdc, 2, 2, iconCurrent);\r
8299     }\r
8300   }\r
8301   (void) ReleaseDC(hwndMain, hdc);\r
8302   if (hwndConsole)\r
8303     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8304 }\r
8305 \r
8306 \r
8307 int\r
8308 LoadGameTimerRunning()\r
8309 {\r
8310   return loadGameTimerEvent != 0;\r
8311 }\r
8312 \r
8313 int\r
8314 StopLoadGameTimer()\r
8315 {\r
8316   if (loadGameTimerEvent == 0) return FALSE;\r
8317   KillTimer(hwndMain, loadGameTimerEvent);\r
8318   loadGameTimerEvent = 0;\r
8319   return TRUE;\r
8320 }\r
8321 \r
8322 void\r
8323 StartLoadGameTimer(long millisec)\r
8324 {\r
8325   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8326                                 (UINT) millisec, NULL);\r
8327 }\r
8328 \r
8329 void\r
8330 AutoSaveGame()\r
8331 {\r
8332   char *defName;\r
8333   FILE *f;\r
8334   char fileTitle[MSG_SIZ];\r
8335 \r
8336   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8337   f = OpenFileDialog(hwndMain, "a", defName,\r
8338                      appData.oldSaveStyle ? "gam" : "pgn",\r
8339                      GAME_FILT, \r
8340                      "Save Game to File", NULL, fileTitle, NULL);\r
8341   if (f != NULL) {\r
8342     SaveGame(f, 0, "");\r
8343     fclose(f);\r
8344   }\r
8345 }\r
8346 \r
8347 \r
8348 void\r
8349 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8350 {\r
8351   if (delayedTimerEvent != 0) {\r
8352     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8353       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8354     }\r
8355     KillTimer(hwndMain, delayedTimerEvent);\r
8356     delayedTimerEvent = 0;\r
8357     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8358     delayedTimerCallback();\r
8359   }\r
8360   delayedTimerCallback = cb;\r
8361   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8362                                 (UINT) millisec, NULL);\r
8363 }\r
8364 \r
8365 DelayedEventCallback\r
8366 GetDelayedEvent()\r
8367 {\r
8368   if (delayedTimerEvent) {\r
8369     return delayedTimerCallback;\r
8370   } else {\r
8371     return NULL;\r
8372   }\r
8373 }\r
8374 \r
8375 void\r
8376 CancelDelayedEvent()\r
8377 {\r
8378   if (delayedTimerEvent) {\r
8379     KillTimer(hwndMain, delayedTimerEvent);\r
8380     delayedTimerEvent = 0;\r
8381   }\r
8382 }\r
8383 \r
8384 DWORD GetWin32Priority(int nice)\r
8385 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8386 /*\r
8387 REALTIME_PRIORITY_CLASS     0x00000100\r
8388 HIGH_PRIORITY_CLASS         0x00000080\r
8389 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8390 NORMAL_PRIORITY_CLASS       0x00000020\r
8391 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8392 IDLE_PRIORITY_CLASS         0x00000040\r
8393 */\r
8394         if (nice < -15) return 0x00000080;\r
8395         if (nice < 0)   return 0x00008000;\r
8396         if (nice == 0)  return 0x00000020;\r
8397         if (nice < 15)  return 0x00004000;\r
8398         return 0x00000040;\r
8399 }\r
8400 \r
8401 /* Start a child process running the given program.\r
8402    The process's standard output can be read from "from", and its\r
8403    standard input can be written to "to".\r
8404    Exit with fatal error if anything goes wrong.\r
8405    Returns an opaque pointer that can be used to destroy the process\r
8406    later.\r
8407 */\r
8408 int\r
8409 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8410 {\r
8411 #define BUFSIZE 4096\r
8412 \r
8413   HANDLE hChildStdinRd, hChildStdinWr,\r
8414     hChildStdoutRd, hChildStdoutWr;\r
8415   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8416   SECURITY_ATTRIBUTES saAttr;\r
8417   BOOL fSuccess;\r
8418   PROCESS_INFORMATION piProcInfo;\r
8419   STARTUPINFO siStartInfo;\r
8420   ChildProc *cp;\r
8421   char buf[MSG_SIZ];\r
8422   DWORD err;\r
8423 \r
8424   if (appData.debugMode) {\r
8425     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8426   }\r
8427 \r
8428   *pr = NoProc;\r
8429 \r
8430   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8431   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8432   saAttr.bInheritHandle = TRUE;\r
8433   saAttr.lpSecurityDescriptor = NULL;\r
8434 \r
8435   /*\r
8436    * The steps for redirecting child's STDOUT:\r
8437    *     1. Create anonymous pipe to be STDOUT for child.\r
8438    *     2. Create a noninheritable duplicate of read handle,\r
8439    *         and close the inheritable read handle.\r
8440    */\r
8441 \r
8442   /* Create a pipe for the child's STDOUT. */\r
8443   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8444     return GetLastError();\r
8445   }\r
8446 \r
8447   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8448   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8449                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8450                              FALSE,     /* not inherited */\r
8451                              DUPLICATE_SAME_ACCESS);\r
8452   if (! fSuccess) {\r
8453     return GetLastError();\r
8454   }\r
8455   CloseHandle(hChildStdoutRd);\r
8456 \r
8457   /*\r
8458    * The steps for redirecting child's STDIN:\r
8459    *     1. Create anonymous pipe to be STDIN for child.\r
8460    *     2. Create a noninheritable duplicate of write handle,\r
8461    *         and close the inheritable write handle.\r
8462    */\r
8463 \r
8464   /* Create a pipe for the child's STDIN. */\r
8465   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8466     return GetLastError();\r
8467   }\r
8468 \r
8469   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8470   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8471                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8472                              FALSE,     /* not inherited */\r
8473                              DUPLICATE_SAME_ACCESS);\r
8474   if (! fSuccess) {\r
8475     return GetLastError();\r
8476   }\r
8477   CloseHandle(hChildStdinWr);\r
8478 \r
8479   /* Arrange to (1) look in dir for the child .exe file, and\r
8480    * (2) have dir be the child's working directory.  Interpret\r
8481    * dir relative to the directory WinBoard loaded from. */\r
8482   GetCurrentDirectory(MSG_SIZ, buf);\r
8483   SetCurrentDirectory(installDir);\r
8484   SetCurrentDirectory(dir);\r
8485 \r
8486   /* Now create the child process. */\r
8487 \r
8488   siStartInfo.cb = sizeof(STARTUPINFO);\r
8489   siStartInfo.lpReserved = NULL;\r
8490   siStartInfo.lpDesktop = NULL;\r
8491   siStartInfo.lpTitle = NULL;\r
8492   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8493   siStartInfo.cbReserved2 = 0;\r
8494   siStartInfo.lpReserved2 = NULL;\r
8495   siStartInfo.hStdInput = hChildStdinRd;\r
8496   siStartInfo.hStdOutput = hChildStdoutWr;\r
8497   siStartInfo.hStdError = hChildStdoutWr;\r
8498 \r
8499   fSuccess = CreateProcess(NULL,\r
8500                            cmdLine,        /* command line */\r
8501                            NULL,           /* process security attributes */\r
8502                            NULL,           /* primary thread security attrs */\r
8503                            TRUE,           /* handles are inherited */\r
8504                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8505                            NULL,           /* use parent's environment */\r
8506                            NULL,\r
8507                            &siStartInfo, /* STARTUPINFO pointer */\r
8508                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8509 \r
8510   err = GetLastError();\r
8511   SetCurrentDirectory(buf); /* return to prev directory */\r
8512   if (! fSuccess) {\r
8513     return err;\r
8514   }\r
8515 \r
8516   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8517     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8518     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8519   }\r
8520 \r
8521   /* Close the handles we don't need in the parent */\r
8522   CloseHandle(piProcInfo.hThread);\r
8523   CloseHandle(hChildStdinRd);\r
8524   CloseHandle(hChildStdoutWr);\r
8525 \r
8526   /* Prepare return value */\r
8527   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8528   cp->kind = CPReal;\r
8529   cp->hProcess = piProcInfo.hProcess;\r
8530   cp->pid = piProcInfo.dwProcessId;\r
8531   cp->hFrom = hChildStdoutRdDup;\r
8532   cp->hTo = hChildStdinWrDup;\r
8533 \r
8534   *pr = (void *) cp;\r
8535 \r
8536   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8537      2000 where engines sometimes don't see the initial command(s)\r
8538      from WinBoard and hang.  I don't understand how that can happen,\r
8539      but the Sleep is harmless, so I've put it in.  Others have also\r
8540      reported what may be the same problem, so hopefully this will fix\r
8541      it for them too.  */\r
8542   Sleep(500);\r
8543 \r
8544   return NO_ERROR;\r
8545 }\r
8546 \r
8547 \r
8548 void\r
8549 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8550 {\r
8551   ChildProc *cp; int result;\r
8552 \r
8553   cp = (ChildProc *) pr;\r
8554   if (cp == NULL) return;\r
8555 \r
8556   switch (cp->kind) {\r
8557   case CPReal:\r
8558     /* TerminateProcess is considered harmful, so... */\r
8559     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8560     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8561     /* The following doesn't work because the chess program\r
8562        doesn't "have the same console" as WinBoard.  Maybe\r
8563        we could arrange for this even though neither WinBoard\r
8564        nor the chess program uses a console for stdio? */\r
8565     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8566 \r
8567     /* [AS] Special termination modes for misbehaving programs... */\r
8568     if( signal == 9 ) { \r
8569         result = TerminateProcess( cp->hProcess, 0 );\r
8570 \r
8571         if ( appData.debugMode) {\r
8572             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8573         }\r
8574     }\r
8575     else if( signal == 10 ) {\r
8576         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8577 \r
8578         if( dw != WAIT_OBJECT_0 ) {\r
8579             result = TerminateProcess( cp->hProcess, 0 );\r
8580 \r
8581             if ( appData.debugMode) {\r
8582                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8583             }\r
8584 \r
8585         }\r
8586     }\r
8587 \r
8588     CloseHandle(cp->hProcess);\r
8589     break;\r
8590 \r
8591   case CPComm:\r
8592     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8593     break;\r
8594 \r
8595   case CPSock:\r
8596     closesocket(cp->sock);\r
8597     WSACleanup();\r
8598     break;\r
8599 \r
8600   case CPRcmd:\r
8601     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8602     closesocket(cp->sock);\r
8603     closesocket(cp->sock2);\r
8604     WSACleanup();\r
8605     break;\r
8606   }\r
8607   free(cp);\r
8608 }\r
8609 \r
8610 void\r
8611 InterruptChildProcess(ProcRef pr)\r
8612 {\r
8613   ChildProc *cp;\r
8614 \r
8615   cp = (ChildProc *) pr;\r
8616   if (cp == NULL) return;\r
8617   switch (cp->kind) {\r
8618   case CPReal:\r
8619     /* The following doesn't work because the chess program\r
8620        doesn't "have the same console" as WinBoard.  Maybe\r
8621        we could arrange for this even though neither WinBoard\r
8622        nor the chess program uses a console for stdio */\r
8623     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8624     break;\r
8625 \r
8626   case CPComm:\r
8627   case CPSock:\r
8628     /* Can't interrupt */\r
8629     break;\r
8630 \r
8631   case CPRcmd:\r
8632     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8633     break;\r
8634   }\r
8635 }\r
8636 \r
8637 \r
8638 int\r
8639 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8640 {\r
8641   char cmdLine[MSG_SIZ];\r
8642 \r
8643   if (port[0] == NULLCHAR) {\r
8644     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8645   } else {\r
8646     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8647   }\r
8648   return StartChildProcess(cmdLine, "", pr);\r
8649 }\r
8650 \r
8651 \r
8652 /* Code to open TCP sockets */\r
8653 \r
8654 int\r
8655 OpenTCP(char *host, char *port, ProcRef *pr)\r
8656 {\r
8657   ChildProc *cp;\r
8658   int err;\r
8659   SOCKET s;\r
8660   struct sockaddr_in sa, mysa;\r
8661   struct hostent FAR *hp;\r
8662   unsigned short uport;\r
8663   WORD wVersionRequested;\r
8664   WSADATA wsaData;\r
8665 \r
8666   /* Initialize socket DLL */\r
8667   wVersionRequested = MAKEWORD(1, 1);\r
8668   err = WSAStartup(wVersionRequested, &wsaData);\r
8669   if (err != 0) return err;\r
8670 \r
8671   /* Make socket */\r
8672   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8673     err = WSAGetLastError();\r
8674     WSACleanup();\r
8675     return err;\r
8676   }\r
8677 \r
8678   /* Bind local address using (mostly) don't-care values.\r
8679    */\r
8680   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8681   mysa.sin_family = AF_INET;\r
8682   mysa.sin_addr.s_addr = INADDR_ANY;\r
8683   uport = (unsigned short) 0;\r
8684   mysa.sin_port = htons(uport);\r
8685   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8686       == SOCKET_ERROR) {\r
8687     err = WSAGetLastError();\r
8688     WSACleanup();\r
8689     return err;\r
8690   }\r
8691 \r
8692   /* Resolve remote host name */\r
8693   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8694   if (!(hp = gethostbyname(host))) {\r
8695     unsigned int b0, b1, b2, b3;\r
8696 \r
8697     err = WSAGetLastError();\r
8698 \r
8699     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8700       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8701       hp->h_addrtype = AF_INET;\r
8702       hp->h_length = 4;\r
8703       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8704       hp->h_addr_list[0] = (char *) malloc(4);\r
8705       hp->h_addr_list[0][0] = (char) b0;\r
8706       hp->h_addr_list[0][1] = (char) b1;\r
8707       hp->h_addr_list[0][2] = (char) b2;\r
8708       hp->h_addr_list[0][3] = (char) b3;\r
8709     } else {\r
8710       WSACleanup();\r
8711       return err;\r
8712     }\r
8713   }\r
8714   sa.sin_family = hp->h_addrtype;\r
8715   uport = (unsigned short) atoi(port);\r
8716   sa.sin_port = htons(uport);\r
8717   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8718 \r
8719   /* Make connection */\r
8720   if (connect(s, (struct sockaddr *) &sa,\r
8721               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8722     err = WSAGetLastError();\r
8723     WSACleanup();\r
8724     return err;\r
8725   }\r
8726 \r
8727   /* Prepare return value */\r
8728   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8729   cp->kind = CPSock;\r
8730   cp->sock = s;\r
8731   *pr = (ProcRef *) cp;\r
8732 \r
8733   return NO_ERROR;\r
8734 }\r
8735 \r
8736 int\r
8737 OpenCommPort(char *name, ProcRef *pr)\r
8738 {\r
8739   HANDLE h;\r
8740   COMMTIMEOUTS ct;\r
8741   ChildProc *cp;\r
8742   char fullname[MSG_SIZ];\r
8743 \r
8744   if (*name != '\\')\r
8745     sprintf(fullname, "\\\\.\\%s", name);\r
8746   else\r
8747     strcpy(fullname, name);\r
8748 \r
8749   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8750                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8751   if (h == (HANDLE) -1) {\r
8752     return GetLastError();\r
8753   }\r
8754   hCommPort = h;\r
8755 \r
8756   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8757 \r
8758   /* Accumulate characters until a 100ms pause, then parse */\r
8759   ct.ReadIntervalTimeout = 100;\r
8760   ct.ReadTotalTimeoutMultiplier = 0;\r
8761   ct.ReadTotalTimeoutConstant = 0;\r
8762   ct.WriteTotalTimeoutMultiplier = 0;\r
8763   ct.WriteTotalTimeoutConstant = 0;\r
8764   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8765 \r
8766   /* Prepare return value */\r
8767   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8768   cp->kind = CPComm;\r
8769   cp->hFrom = h;\r
8770   cp->hTo = h;\r
8771   *pr = (ProcRef *) cp;\r
8772 \r
8773   return NO_ERROR;\r
8774 }\r
8775 \r
8776 int\r
8777 OpenLoopback(ProcRef *pr)\r
8778 {\r
8779   DisplayFatalError("Not implemented", 0, 1);\r
8780   return NO_ERROR;\r
8781 }\r
8782 \r
8783 \r
8784 int\r
8785 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8786 {\r
8787   ChildProc *cp;\r
8788   int err;\r
8789   SOCKET s, s2, s3;\r
8790   struct sockaddr_in sa, mysa;\r
8791   struct hostent FAR *hp;\r
8792   unsigned short uport;\r
8793   WORD wVersionRequested;\r
8794   WSADATA wsaData;\r
8795   int fromPort;\r
8796   char stderrPortStr[MSG_SIZ];\r
8797 \r
8798   /* Initialize socket DLL */\r
8799   wVersionRequested = MAKEWORD(1, 1);\r
8800   err = WSAStartup(wVersionRequested, &wsaData);\r
8801   if (err != 0) return err;\r
8802 \r
8803   /* Resolve remote host name */\r
8804   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8805   if (!(hp = gethostbyname(host))) {\r
8806     unsigned int b0, b1, b2, b3;\r
8807 \r
8808     err = WSAGetLastError();\r
8809 \r
8810     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8811       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8812       hp->h_addrtype = AF_INET;\r
8813       hp->h_length = 4;\r
8814       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8815       hp->h_addr_list[0] = (char *) malloc(4);\r
8816       hp->h_addr_list[0][0] = (char) b0;\r
8817       hp->h_addr_list[0][1] = (char) b1;\r
8818       hp->h_addr_list[0][2] = (char) b2;\r
8819       hp->h_addr_list[0][3] = (char) b3;\r
8820     } else {\r
8821       WSACleanup();\r
8822       return err;\r
8823     }\r
8824   }\r
8825   sa.sin_family = hp->h_addrtype;\r
8826   uport = (unsigned short) 514;\r
8827   sa.sin_port = htons(uport);\r
8828   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8829 \r
8830   /* Bind local socket to unused "privileged" port address\r
8831    */\r
8832   s = INVALID_SOCKET;\r
8833   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8834   mysa.sin_family = AF_INET;\r
8835   mysa.sin_addr.s_addr = INADDR_ANY;\r
8836   for (fromPort = 1023;; fromPort--) {\r
8837     if (fromPort < 0) {\r
8838       WSACleanup();\r
8839       return WSAEADDRINUSE;\r
8840     }\r
8841     if (s == INVALID_SOCKET) {\r
8842       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8843         err = WSAGetLastError();\r
8844         WSACleanup();\r
8845         return err;\r
8846       }\r
8847     }\r
8848     uport = (unsigned short) fromPort;\r
8849     mysa.sin_port = htons(uport);\r
8850     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8851         == SOCKET_ERROR) {\r
8852       err = WSAGetLastError();\r
8853       if (err == WSAEADDRINUSE) continue;\r
8854       WSACleanup();\r
8855       return err;\r
8856     }\r
8857     if (connect(s, (struct sockaddr *) &sa,\r
8858       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8859       err = WSAGetLastError();\r
8860       if (err == WSAEADDRINUSE) {\r
8861         closesocket(s);\r
8862         s = -1;\r
8863         continue;\r
8864       }\r
8865       WSACleanup();\r
8866       return err;\r
8867     }\r
8868     break;\r
8869   }\r
8870 \r
8871   /* Bind stderr local socket to unused "privileged" port address\r
8872    */\r
8873   s2 = INVALID_SOCKET;\r
8874   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8875   mysa.sin_family = AF_INET;\r
8876   mysa.sin_addr.s_addr = INADDR_ANY;\r
8877   for (fromPort = 1023;; fromPort--) {\r
8878     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8879     if (fromPort < 0) {\r
8880       (void) closesocket(s);\r
8881       WSACleanup();\r
8882       return WSAEADDRINUSE;\r
8883     }\r
8884     if (s2 == INVALID_SOCKET) {\r
8885       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8886         err = WSAGetLastError();\r
8887         closesocket(s);\r
8888         WSACleanup();\r
8889         return err;\r
8890       }\r
8891     }\r
8892     uport = (unsigned short) fromPort;\r
8893     mysa.sin_port = htons(uport);\r
8894     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8895         == SOCKET_ERROR) {\r
8896       err = WSAGetLastError();\r
8897       if (err == WSAEADDRINUSE) continue;\r
8898       (void) closesocket(s);\r
8899       WSACleanup();\r
8900       return err;\r
8901     }\r
8902     if (listen(s2, 1) == SOCKET_ERROR) {\r
8903       err = WSAGetLastError();\r
8904       if (err == WSAEADDRINUSE) {\r
8905         closesocket(s2);\r
8906         s2 = INVALID_SOCKET;\r
8907         continue;\r
8908       }\r
8909       (void) closesocket(s);\r
8910       (void) closesocket(s2);\r
8911       WSACleanup();\r
8912       return err;\r
8913     }\r
8914     break;\r
8915   }\r
8916   prevStderrPort = fromPort; // remember port used\r
8917   sprintf(stderrPortStr, "%d", fromPort);\r
8918 \r
8919   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
8920     err = WSAGetLastError();\r
8921     (void) closesocket(s);\r
8922     (void) closesocket(s2);\r
8923     WSACleanup();\r
8924     return err;\r
8925   }\r
8926 \r
8927   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
8928     err = WSAGetLastError();\r
8929     (void) closesocket(s);\r
8930     (void) closesocket(s2);\r
8931     WSACleanup();\r
8932     return err;\r
8933   }\r
8934   if (*user == NULLCHAR) user = UserName();\r
8935   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
8936     err = WSAGetLastError();\r
8937     (void) closesocket(s);\r
8938     (void) closesocket(s2);\r
8939     WSACleanup();\r
8940     return err;\r
8941   }\r
8942   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
8943     err = WSAGetLastError();\r
8944     (void) closesocket(s);\r
8945     (void) closesocket(s2);\r
8946     WSACleanup();\r
8947     return err;\r
8948   }\r
8949 \r
8950   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
8951     err = WSAGetLastError();\r
8952     (void) closesocket(s);\r
8953     (void) closesocket(s2);\r
8954     WSACleanup();\r
8955     return err;\r
8956   }\r
8957   (void) closesocket(s2);  /* Stop listening */\r
8958 \r
8959   /* Prepare return value */\r
8960   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8961   cp->kind = CPRcmd;\r
8962   cp->sock = s;\r
8963   cp->sock2 = s3;\r
8964   *pr = (ProcRef *) cp;\r
8965 \r
8966   return NO_ERROR;\r
8967 }\r
8968 \r
8969 \r
8970 InputSourceRef\r
8971 AddInputSource(ProcRef pr, int lineByLine,\r
8972                InputCallback func, VOIDSTAR closure)\r
8973 {\r
8974   InputSource *is, *is2 = NULL;\r
8975   ChildProc *cp = (ChildProc *) pr;\r
8976 \r
8977   is = (InputSource *) calloc(1, sizeof(InputSource));\r
8978   is->lineByLine = lineByLine;\r
8979   is->func = func;\r
8980   is->closure = closure;\r
8981   is->second = NULL;\r
8982   is->next = is->buf;\r
8983   if (pr == NoProc) {\r
8984     is->kind = CPReal;\r
8985     consoleInputSource = is;\r
8986   } else {\r
8987     is->kind = cp->kind;\r
8988     /* \r
8989         [AS] Try to avoid a race condition if the thread is given control too early:\r
8990         we create all threads suspended so that the is->hThread variable can be\r
8991         safely assigned, then let the threads start with ResumeThread.\r
8992     */\r
8993     switch (cp->kind) {\r
8994     case CPReal:\r
8995       is->hFile = cp->hFrom;\r
8996       cp->hFrom = NULL; /* now owned by InputThread */\r
8997       is->hThread =\r
8998         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
8999                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9000       break;\r
9001 \r
9002     case CPComm:\r
9003       is->hFile = cp->hFrom;\r
9004       cp->hFrom = NULL; /* now owned by InputThread */\r
9005       is->hThread =\r
9006         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9007                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9008       break;\r
9009 \r
9010     case CPSock:\r
9011       is->sock = cp->sock;\r
9012       is->hThread =\r
9013         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9014                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9015       break;\r
9016 \r
9017     case CPRcmd:\r
9018       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9019       *is2 = *is;\r
9020       is->sock = cp->sock;\r
9021       is->second = is2;\r
9022       is2->sock = cp->sock2;\r
9023       is2->second = is2;\r
9024       is->hThread =\r
9025         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9026                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9027       is2->hThread =\r
9028         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9029                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9030       break;\r
9031     }\r
9032 \r
9033     if( is->hThread != NULL ) {\r
9034         ResumeThread( is->hThread );\r
9035     }\r
9036 \r
9037     if( is2 != NULL && is2->hThread != NULL ) {\r
9038         ResumeThread( is2->hThread );\r
9039     }\r
9040   }\r
9041 \r
9042   return (InputSourceRef) is;\r
9043 }\r
9044 \r
9045 void\r
9046 RemoveInputSource(InputSourceRef isr)\r
9047 {\r
9048   InputSource *is;\r
9049 \r
9050   is = (InputSource *) isr;\r
9051   is->hThread = NULL;  /* tell thread to stop */\r
9052   CloseHandle(is->hThread);\r
9053   if (is->second != NULL) {\r
9054     is->second->hThread = NULL;\r
9055     CloseHandle(is->second->hThread);\r
9056   }\r
9057 }\r
9058 \r
9059 int no_wrap(char *message, int count)\r
9060 {\r
9061     ConsoleOutput(message, count, FALSE);\r
9062     return count;\r
9063 }\r
9064 \r
9065 int\r
9066 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9067 {\r
9068   DWORD dOutCount;\r
9069   int outCount = SOCKET_ERROR;\r
9070   ChildProc *cp = (ChildProc *) pr;\r
9071   static OVERLAPPED ovl;\r
9072   static int line = 0;\r
9073 \r
9074   if (pr == NoProc)\r
9075   {\r
9076     if (appData.noJoin || !appData.useInternalWrap)\r
9077       return no_wrap(message, count);\r
9078     else\r
9079     {\r
9080       int width = get_term_width();\r
9081       int len = wrap(NULL, message, count, width, &line);\r
9082       char *msg = malloc(len);\r
9083       int dbgchk;\r
9084 \r
9085       if (!msg)\r
9086         return no_wrap(message, count);\r
9087       else\r
9088       {\r
9089         dbgchk = wrap(msg, message, count, width, &line);\r
9090         if (dbgchk != len && appData.debugMode)\r
9091             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9092         ConsoleOutput(msg, len, FALSE);\r
9093         free(msg);\r
9094         return len;\r
9095       }\r
9096     }\r
9097   }\r
9098 \r
9099   if (ovl.hEvent == NULL) {\r
9100     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9101   }\r
9102   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9103 \r
9104   switch (cp->kind) {\r
9105   case CPSock:\r
9106   case CPRcmd:\r
9107     outCount = send(cp->sock, message, count, 0);\r
9108     if (outCount == SOCKET_ERROR) {\r
9109       *outError = WSAGetLastError();\r
9110     } else {\r
9111       *outError = NO_ERROR;\r
9112     }\r
9113     break;\r
9114 \r
9115   case CPReal:\r
9116     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9117                   &dOutCount, NULL)) {\r
9118       *outError = NO_ERROR;\r
9119       outCount = (int) dOutCount;\r
9120     } else {\r
9121       *outError = GetLastError();\r
9122     }\r
9123     break;\r
9124 \r
9125   case CPComm:\r
9126     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9127                             &dOutCount, &ovl);\r
9128     if (*outError == NO_ERROR) {\r
9129       outCount = (int) dOutCount;\r
9130     }\r
9131     break;\r
9132   }\r
9133   return outCount;\r
9134 }\r
9135 \r
9136 int\r
9137 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9138                        long msdelay)\r
9139 {\r
9140   /* Ignore delay, not implemented for WinBoard */\r
9141   return OutputToProcess(pr, message, count, outError);\r
9142 }\r
9143 \r
9144 \r
9145 void\r
9146 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9147                         char *buf, int count, int error)\r
9148 {\r
9149   DisplayFatalError("Not implemented", 0, 1);\r
9150 }\r
9151 \r
9152 /* see wgamelist.c for Game List functions */\r
9153 /* see wedittags.c for Edit Tags functions */\r
9154 \r
9155 \r
9156 VOID\r
9157 ICSInitScript()\r
9158 {\r
9159   FILE *f;\r
9160   char buf[MSG_SIZ];\r
9161   char *dummy;\r
9162 \r
9163   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9164     f = fopen(buf, "r");\r
9165     if (f != NULL) {\r
9166       ProcessICSInitScript(f);\r
9167       fclose(f);\r
9168     }\r
9169   }\r
9170 }\r
9171 \r
9172 \r
9173 VOID\r
9174 StartAnalysisClock()\r
9175 {\r
9176   if (analysisTimerEvent) return;\r
9177   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9178                                         (UINT) 2000, NULL);\r
9179 }\r
9180 \r
9181 VOID\r
9182 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9183 {\r
9184   highlightInfo.sq[0].x = fromX;\r
9185   highlightInfo.sq[0].y = fromY;\r
9186   highlightInfo.sq[1].x = toX;\r
9187   highlightInfo.sq[1].y = toY;\r
9188 }\r
9189 \r
9190 VOID\r
9191 ClearHighlights()\r
9192 {\r
9193   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9194     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9195 }\r
9196 \r
9197 VOID\r
9198 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9199 {\r
9200   premoveHighlightInfo.sq[0].x = fromX;\r
9201   premoveHighlightInfo.sq[0].y = fromY;\r
9202   premoveHighlightInfo.sq[1].x = toX;\r
9203   premoveHighlightInfo.sq[1].y = toY;\r
9204 }\r
9205 \r
9206 VOID\r
9207 ClearPremoveHighlights()\r
9208 {\r
9209   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9210     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9211 }\r
9212 \r
9213 VOID\r
9214 ShutDownFrontEnd()\r
9215 {\r
9216   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9217   DeleteClipboardTempFiles();\r
9218 }\r
9219 \r
9220 void\r
9221 BoardToTop()\r
9222 {\r
9223     if (IsIconic(hwndMain))\r
9224       ShowWindow(hwndMain, SW_RESTORE);\r
9225 \r
9226     SetActiveWindow(hwndMain);\r
9227 }\r
9228 \r
9229 /*\r
9230  * Prototypes for animation support routines\r
9231  */\r
9232 static void ScreenSquare(int column, int row, POINT * pt);\r
9233 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9234      POINT frames[], int * nFrames);\r
9235 \r
9236 \r
9237 void\r
9238 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9239 {       // [HGM] atomic: animate blast wave\r
9240         int i;\r
9241 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9242         explodeInfo.fromX = fromX;\r
9243         explodeInfo.fromY = fromY;\r
9244         explodeInfo.toX = toX;\r
9245         explodeInfo.toY = toY;\r
9246         for(i=1; i<nFrames; i++) {\r
9247             explodeInfo.radius = (i*180)/(nFrames-1);\r
9248             DrawPosition(FALSE, NULL);\r
9249             Sleep(appData.animSpeed);\r
9250         }\r
9251         explodeInfo.radius = 0;\r
9252         DrawPosition(TRUE, NULL);\r
9253 }\r
9254 \r
9255 #define kFactor 4\r
9256 \r
9257 void\r
9258 AnimateMove(board, fromX, fromY, toX, toY)\r
9259      Board board;\r
9260      int fromX;\r
9261      int fromY;\r
9262      int toX;\r
9263      int toY;\r
9264 {\r
9265   ChessSquare piece;\r
9266   POINT start, finish, mid;\r
9267   POINT frames[kFactor * 2 + 1];\r
9268   int nFrames, n;\r
9269 \r
9270   if (!appData.animate) return;\r
9271   if (doingSizing) return;\r
9272   if (fromY < 0 || fromX < 0) return;\r
9273   piece = board[fromY][fromX];\r
9274   if (piece >= EmptySquare) return;\r
9275 \r
9276   ScreenSquare(fromX, fromY, &start);\r
9277   ScreenSquare(toX, toY, &finish);\r
9278 \r
9279   /* All pieces except knights move in straight line */\r
9280   if (piece != WhiteKnight && piece != BlackKnight) {\r
9281     mid.x = start.x + (finish.x - start.x) / 2;\r
9282     mid.y = start.y + (finish.y - start.y) / 2;\r
9283   } else {\r
9284     /* Knight: make diagonal movement then straight */\r
9285     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9286        mid.x = start.x + (finish.x - start.x) / 2;\r
9287        mid.y = finish.y;\r
9288      } else {\r
9289        mid.x = finish.x;\r
9290        mid.y = start.y + (finish.y - start.y) / 2;\r
9291      }\r
9292   }\r
9293   \r
9294   /* Don't use as many frames for very short moves */\r
9295   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9296     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9297   else\r
9298     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9299 \r
9300   animInfo.from.x = fromX;\r
9301   animInfo.from.y = fromY;\r
9302   animInfo.to.x = toX;\r
9303   animInfo.to.y = toY;\r
9304   animInfo.lastpos = start;\r
9305   animInfo.piece = piece;\r
9306   for (n = 0; n < nFrames; n++) {\r
9307     animInfo.pos = frames[n];\r
9308     DrawPosition(FALSE, NULL);\r
9309     animInfo.lastpos = animInfo.pos;\r
9310     Sleep(appData.animSpeed);\r
9311   }\r
9312   animInfo.pos = finish;\r
9313   DrawPosition(FALSE, NULL);\r
9314   animInfo.piece = EmptySquare;\r
9315   if(gameInfo.variant == VariantAtomic && \r
9316      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9317         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9318 }\r
9319 \r
9320 /*      Convert board position to corner of screen rect and color       */\r
9321 \r
9322 static void\r
9323 ScreenSquare(column, row, pt)\r
9324      int column; int row; POINT * pt;\r
9325 {\r
9326   if (flipView) {\r
9327     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9328     pt->y = lineGap + row * (squareSize + lineGap);\r
9329   } else {\r
9330     pt->x = lineGap + column * (squareSize + lineGap);\r
9331     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9332   }\r
9333 }\r
9334 \r
9335 /*      Generate a series of frame coords from start->mid->finish.\r
9336         The movement rate doubles until the half way point is\r
9337         reached, then halves back down to the final destination,\r
9338         which gives a nice slow in/out effect. The algorithmn\r
9339         may seem to generate too many intermediates for short\r
9340         moves, but remember that the purpose is to attract the\r
9341         viewers attention to the piece about to be moved and\r
9342         then to where it ends up. Too few frames would be less\r
9343         noticeable.                                             */\r
9344 \r
9345 static void\r
9346 Tween(start, mid, finish, factor, frames, nFrames)\r
9347      POINT * start; POINT * mid;\r
9348      POINT * finish; int factor;\r
9349      POINT frames[]; int * nFrames;\r
9350 {\r
9351   int n, fraction = 1, count = 0;\r
9352 \r
9353   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9354   for (n = 0; n < factor; n++)\r
9355     fraction *= 2;\r
9356   for (n = 0; n < factor; n++) {\r
9357     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9358     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9359     count ++;\r
9360     fraction = fraction / 2;\r
9361   }\r
9362   \r
9363   /* Midpoint */\r
9364   frames[count] = *mid;\r
9365   count ++;\r
9366   \r
9367   /* Slow out, stepping 1/2, then 1/4, ... */\r
9368   fraction = 2;\r
9369   for (n = 0; n < factor; n++) {\r
9370     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9371     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9372     count ++;\r
9373     fraction = fraction * 2;\r
9374   }\r
9375   *nFrames = count;\r
9376 }\r
9377 \r
9378 void\r
9379 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9380 {\r
9381     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9382 \r
9383     EvalGraphSet( first, last, current, pvInfoList );\r
9384 }\r