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