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