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