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