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