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