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