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