e4e8db961a111e5e868d1cae9a879b50bbd2ee25
[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 || gameInfo.variant >= VariantShogi)) {\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       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2535         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2536       else\r
2537         BitBlt( hdc,\r
2538             x, y,\r
2539             squareSize, squareSize,\r
2540             tmphdc,\r
2541             0, 0,\r
2542             SRCAND );\r
2543 \r
2544         SelectObject( tmphdc, hPieceFace[ index ] );\r
2545 \r
2546       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2547         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2548       else\r
2549         BitBlt( hdc,\r
2550             x, y,\r
2551             squareSize, squareSize,\r
2552             tmphdc,\r
2553             0, 0,\r
2554             SRCPAINT );\r
2555 \r
2556         return;\r
2557     }\r
2558   }\r
2559 \r
2560   if (appData.monoMode) {\r
2561     SelectObject(tmphdc, PieceBitmap(piece, \r
2562       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2563     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2564            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2565   } else {\r
2566     tmpSize = squareSize;\r
2567     if(minorSize &&\r
2568         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2569          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2570       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2571       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2572       x += (squareSize - minorSize)>>1;\r
2573       y += squareSize - minorSize - 2;\r
2574       tmpSize = minorSize;\r
2575     }\r
2576     if (color || appData.allWhite ) {\r
2577       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2578       if( color )\r
2579               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2580       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2581       if(appData.upsideDown && color==flipView)\r
2582         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2583       else\r
2584         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2585       /* Use black for outline of white pieces */\r
2586       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2587       if(appData.upsideDown && color==flipView)\r
2588         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2589       else\r
2590         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2591     } else {\r
2592       /* Use square color for details of black pieces */\r
2593       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2594       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2595       if(appData.upsideDown && !flipView)\r
2596         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2597       else\r
2598         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2599     }\r
2600     SelectObject(hdc, oldBrush);\r
2601     SelectObject(tmphdc, oldBitmap);\r
2602   }\r
2603 }\r
2604 \r
2605 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2606 int GetBackTextureMode( int algo )\r
2607 {\r
2608     int result = BACK_TEXTURE_MODE_DISABLED;\r
2609 \r
2610     switch( algo ) \r
2611     {\r
2612         case BACK_TEXTURE_MODE_PLAIN:\r
2613             result = 1; /* Always use identity map */\r
2614             break;\r
2615         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2616             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2617             break;\r
2618     }\r
2619 \r
2620     return result;\r
2621 }\r
2622 \r
2623 /* \r
2624     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2625     to handle redraws cleanly (as random numbers would always be different).\r
2626 */\r
2627 VOID RebuildTextureSquareInfo()\r
2628 {\r
2629     BITMAP bi;\r
2630     int lite_w = 0;\r
2631     int lite_h = 0;\r
2632     int dark_w = 0;\r
2633     int dark_h = 0;\r
2634     int row;\r
2635     int col;\r
2636 \r
2637     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2638 \r
2639     if( liteBackTexture != NULL ) {\r
2640         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2641             lite_w = bi.bmWidth;\r
2642             lite_h = bi.bmHeight;\r
2643         }\r
2644     }\r
2645 \r
2646     if( darkBackTexture != NULL ) {\r
2647         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2648             dark_w = bi.bmWidth;\r
2649             dark_h = bi.bmHeight;\r
2650         }\r
2651     }\r
2652 \r
2653     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2654         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2655             if( (col + row) & 1 ) {\r
2656                 /* Lite square */\r
2657                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2658                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2659                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2660                   else\r
2661                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2662                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2663                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2664                   else\r
2665                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2666                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2667                 }\r
2668             }\r
2669             else {\r
2670                 /* Dark square */\r
2671                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2672                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2673                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2674                   else\r
2675                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2676                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2677                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2678                   else\r
2679                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2680                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2681                 }\r
2682             }\r
2683         }\r
2684     }\r
2685 }\r
2686 \r
2687 /* [AS] Arrow highlighting support */\r
2688 \r
2689 static int A_WIDTH = 5; /* Width of arrow body */\r
2690 \r
2691 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2692 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2693 \r
2694 static double Sqr( double x )\r
2695 {\r
2696     return x*x;\r
2697 }\r
2698 \r
2699 static int Round( double x )\r
2700 {\r
2701     return (int) (x + 0.5);\r
2702 }\r
2703 \r
2704 /* Draw an arrow between two points using current settings */\r
2705 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2706 {\r
2707     POINT arrow[7];\r
2708     double dx, dy, j, k, x, y;\r
2709 \r
2710     if( d_x == s_x ) {\r
2711         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2712 \r
2713         arrow[0].x = s_x + A_WIDTH;\r
2714         arrow[0].y = s_y;\r
2715 \r
2716         arrow[1].x = s_x + A_WIDTH;\r
2717         arrow[1].y = d_y - h;\r
2718 \r
2719         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2720         arrow[2].y = d_y - h;\r
2721 \r
2722         arrow[3].x = d_x;\r
2723         arrow[3].y = d_y;\r
2724 \r
2725         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2726         arrow[4].y = d_y - h;\r
2727 \r
2728         arrow[5].x = s_x - A_WIDTH;\r
2729         arrow[5].y = d_y - h;\r
2730 \r
2731         arrow[6].x = s_x - A_WIDTH;\r
2732         arrow[6].y = s_y;\r
2733     }\r
2734     else if( d_y == s_y ) {\r
2735         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2736 \r
2737         arrow[0].x = s_x;\r
2738         arrow[0].y = s_y + A_WIDTH;\r
2739 \r
2740         arrow[1].x = d_x - w;\r
2741         arrow[1].y = s_y + A_WIDTH;\r
2742 \r
2743         arrow[2].x = d_x - w;\r
2744         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2745 \r
2746         arrow[3].x = d_x;\r
2747         arrow[3].y = d_y;\r
2748 \r
2749         arrow[4].x = d_x - w;\r
2750         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2751 \r
2752         arrow[5].x = d_x - w;\r
2753         arrow[5].y = s_y - A_WIDTH;\r
2754 \r
2755         arrow[6].x = s_x;\r
2756         arrow[6].y = s_y - A_WIDTH;\r
2757     }\r
2758     else {\r
2759         /* [AS] Needed a lot of paper for this! :-) */\r
2760         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2761         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2762   \r
2763         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2764 \r
2765         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2766 \r
2767         x = s_x;\r
2768         y = s_y;\r
2769 \r
2770         arrow[0].x = Round(x - j);\r
2771         arrow[0].y = Round(y + j*dx);\r
2772 \r
2773         arrow[1].x = Round(x + j);\r
2774         arrow[1].y = Round(y - j*dx);\r
2775 \r
2776         if( d_x > s_x ) {\r
2777             x = (double) d_x - k;\r
2778             y = (double) d_y - k*dy;\r
2779         }\r
2780         else {\r
2781             x = (double) d_x + k;\r
2782             y = (double) d_y + k*dy;\r
2783         }\r
2784 \r
2785         arrow[2].x = Round(x + j);\r
2786         arrow[2].y = Round(y - j*dx);\r
2787 \r
2788         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2789         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2790 \r
2791         arrow[4].x = d_x;\r
2792         arrow[4].y = d_y;\r
2793 \r
2794         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2795         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2796 \r
2797         arrow[6].x = Round(x - j);\r
2798         arrow[6].y = Round(y + j*dx);\r
2799     }\r
2800 \r
2801     Polygon( hdc, arrow, 7 );\r
2802 }\r
2803 \r
2804 /* [AS] Draw an arrow between two squares */\r
2805 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2806 {\r
2807     int s_x, s_y, d_x, d_y;\r
2808     HPEN hpen;\r
2809     HPEN holdpen;\r
2810     HBRUSH hbrush;\r
2811     HBRUSH holdbrush;\r
2812     LOGBRUSH stLB;\r
2813 \r
2814     if( s_col == d_col && s_row == d_row ) {\r
2815         return;\r
2816     }\r
2817 \r
2818     /* Get source and destination points */\r
2819     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2820     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2821 \r
2822     if( d_y > s_y ) {\r
2823         d_y += squareSize / 4;\r
2824     }\r
2825     else if( d_y < s_y ) {\r
2826         d_y += 3 * squareSize / 4;\r
2827     }\r
2828     else {\r
2829         d_y += squareSize / 2;\r
2830     }\r
2831 \r
2832     if( d_x > s_x ) {\r
2833         d_x += squareSize / 4;\r
2834     }\r
2835     else if( d_x < s_x ) {\r
2836         d_x += 3 * squareSize / 4;\r
2837     }\r
2838     else {\r
2839         d_x += squareSize / 2;\r
2840     }\r
2841 \r
2842     s_x += squareSize / 2;\r
2843     s_y += squareSize / 2;\r
2844 \r
2845     /* Adjust width */\r
2846     A_WIDTH = squareSize / 14;\r
2847 \r
2848     /* Draw */\r
2849     stLB.lbStyle = BS_SOLID;\r
2850     stLB.lbColor = appData.highlightArrowColor;\r
2851     stLB.lbHatch = 0;\r
2852 \r
2853     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2854     holdpen = SelectObject( hdc, hpen );\r
2855     hbrush = CreateBrushIndirect( &stLB );\r
2856     holdbrush = SelectObject( hdc, hbrush );\r
2857 \r
2858     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2859 \r
2860     SelectObject( hdc, holdpen );\r
2861     SelectObject( hdc, holdbrush );\r
2862     DeleteObject( hpen );\r
2863     DeleteObject( hbrush );\r
2864 }\r
2865 \r
2866 BOOL HasHighlightInfo()\r
2867 {\r
2868     BOOL result = FALSE;\r
2869 \r
2870     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2871         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2872     {\r
2873         result = TRUE;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 BOOL IsDrawArrowEnabled()\r
2880 {\r
2881     BOOL result = FALSE;\r
2882 \r
2883     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2884         result = TRUE;\r
2885     }\r
2886 \r
2887     return result;\r
2888 }\r
2889 \r
2890 VOID DrawArrowHighlight( HDC hdc )\r
2891 {\r
2892     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2893         DrawArrowBetweenSquares( hdc,\r
2894             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2895             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2896     }\r
2897 }\r
2898 \r
2899 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2900 {\r
2901     HRGN result = NULL;\r
2902 \r
2903     if( HasHighlightInfo() ) {\r
2904         int x1, y1, x2, y2;\r
2905         int sx, sy, dx, dy;\r
2906 \r
2907         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2908         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2909 \r
2910         sx = MIN( x1, x2 );\r
2911         sy = MIN( y1, y2 );\r
2912         dx = MAX( x1, x2 ) + squareSize;\r
2913         dy = MAX( y1, y2 ) + squareSize;\r
2914 \r
2915         result = CreateRectRgn( sx, sy, dx, dy );\r
2916     }\r
2917 \r
2918     return result;\r
2919 }\r
2920 \r
2921 /*\r
2922     Warning: this function modifies the behavior of several other functions. \r
2923     \r
2924     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2925     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2926     repaint is scattered all over the place, which is not good for features such as\r
2927     "arrow highlighting" that require a full repaint of the board.\r
2928 \r
2929     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2930     user interaction, when speed is not so important) but especially to avoid errors\r
2931     in the displayed graphics.\r
2932 \r
2933     In such patched places, I always try refer to this function so there is a single\r
2934     place to maintain knowledge.\r
2935     \r
2936     To restore the original behavior, just return FALSE unconditionally.\r
2937 */\r
2938 BOOL IsFullRepaintPreferrable()\r
2939 {\r
2940     BOOL result = FALSE;\r
2941 \r
2942     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2943         /* Arrow may appear on the board */\r
2944         result = TRUE;\r
2945     }\r
2946 \r
2947     return result;\r
2948 }\r
2949 \r
2950 /* \r
2951     This function is called by DrawPosition to know whether a full repaint must\r
2952     be forced or not.\r
2953 \r
2954     Only DrawPosition may directly call this function, which makes use of \r
2955     some state information. Other function should call DrawPosition specifying \r
2956     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2957 */\r
2958 BOOL DrawPositionNeedsFullRepaint()\r
2959 {\r
2960     BOOL result = FALSE;\r
2961 \r
2962     /* \r
2963         Probably a slightly better policy would be to trigger a full repaint\r
2964         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2965         but animation is fast enough that it's difficult to notice.\r
2966     */\r
2967     if( animInfo.piece == EmptySquare ) {\r
2968         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2969             result = TRUE;\r
2970         }\r
2971     }\r
2972 \r
2973     return result;\r
2974 }\r
2975 \r
2976 VOID\r
2977 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2978 {\r
2979   int row, column, x, y, square_color, piece_color;\r
2980   ChessSquare piece;\r
2981   HBRUSH oldBrush;\r
2982   HDC texture_hdc = NULL;\r
2983 \r
2984   /* [AS] Initialize background textures if needed */\r
2985   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2986       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2987       if( backTextureSquareSize != squareSize \r
2988        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2989           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2990           backTextureSquareSize = squareSize;\r
2991           RebuildTextureSquareInfo();\r
2992       }\r
2993 \r
2994       texture_hdc = CreateCompatibleDC( hdc );\r
2995   }\r
2996 \r
2997   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2998     for (column = 0; column < BOARD_WIDTH; column++) {\r
2999   \r
3000       SquareToPos(row, column, &x, &y);\r
3001 \r
3002       piece = board[row][column];\r
3003 \r
3004       square_color = ((column + row) % 2) == 1;\r
3005       if( gameInfo.variant == VariantXiangqi ) {\r
3006           square_color = !InPalace(row, column);\r
3007           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3008           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3009       }\r
3010       piece_color = (int) piece < (int) BlackPawn;\r
3011 \r
3012 \r
3013       /* [HGM] holdings file: light square or black */\r
3014       if(column == BOARD_LEFT-2) {\r
3015             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3016                 square_color = 1;\r
3017             else {\r
3018                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3019                 continue;\r
3020             }\r
3021       } else\r
3022       if(column == BOARD_RGHT + 1 ) {\r
3023             if( row < gameInfo.holdingsSize )\r
3024                 square_color = 1;\r
3025             else {\r
3026                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3027                 continue;\r
3028             }\r
3029       }\r
3030       if(column == BOARD_LEFT-1 ) /* left align */\r
3031             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3032       else if( column == BOARD_RGHT) /* right align */\r
3033             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3034       else\r
3035       if (appData.monoMode) {\r
3036         if (piece == EmptySquare) {\r
3037           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3038                  square_color ? WHITENESS : BLACKNESS);\r
3039         } else {\r
3040           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3041         }\r
3042       } \r
3043       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3044           /* [AS] Draw the square using a texture bitmap */\r
3045           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3046           int r = row, c = column; // [HGM] do not flip board in flipView\r
3047           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3048 \r
3049           DrawTile( x, y, \r
3050               squareSize, squareSize, \r
3051               hdc, \r
3052               texture_hdc,\r
3053               backTextureSquareInfo[r][c].mode,\r
3054               backTextureSquareInfo[r][c].x,\r
3055               backTextureSquareInfo[r][c].y );\r
3056 \r
3057           SelectObject( texture_hdc, hbm );\r
3058 \r
3059           if (piece != EmptySquare) {\r
3060               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3061           }\r
3062       }\r
3063       else {\r
3064         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3065 \r
3066         oldBrush = SelectObject(hdc, brush );\r
3067         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3068         SelectObject(hdc, oldBrush);\r
3069         if (piece != EmptySquare)\r
3070           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3071       }\r
3072     }\r
3073   }\r
3074 \r
3075   if( texture_hdc != NULL ) {\r
3076     DeleteDC( texture_hdc );\r
3077   }\r
3078 }\r
3079 \r
3080 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3081 void fputDW(FILE *f, int x)\r
3082 {\r
3083         fputc(x     & 255, f);\r
3084         fputc(x>>8  & 255, f);\r
3085         fputc(x>>16 & 255, f);\r
3086         fputc(x>>24 & 255, f);\r
3087 }\r
3088 \r
3089 #define MAX_CLIPS 200   /* more than enough */\r
3090 \r
3091 VOID\r
3092 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3093 {\r
3094 //  HBITMAP bufferBitmap;\r
3095   BITMAP bi;\r
3096 //  RECT Rect;\r
3097   HDC tmphdc;\r
3098   HBITMAP hbm;\r
3099   int w = 100, h = 50;\r
3100 \r
3101   if(logo == NULL) return;\r
3102 //  GetClientRect(hwndMain, &Rect);\r
3103 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3104 //                                      Rect.bottom-Rect.top+1);\r
3105   tmphdc = CreateCompatibleDC(hdc);\r
3106   hbm = SelectObject(tmphdc, logo);\r
3107   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3108             w = bi.bmWidth;\r
3109             h = bi.bmHeight;\r
3110   }\r
3111   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3112                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3113   SelectObject(tmphdc, hbm);\r
3114   DeleteDC(tmphdc);\r
3115 }\r
3116 \r
3117 static HDC hdcSeek;\r
3118 \r
3119 // [HGM] seekgraph\r
3120 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3121 {\r
3122     POINT stPt;\r
3123     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3124     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3125     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3126     SelectObject( hdcSeek, hp );\r
3127 }\r
3128 \r
3129 // front-end wrapper for drawing functions to do rectangles\r
3130 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3131 {\r
3132     HPEN hp;\r
3133     RECT rc;\r
3134 \r
3135     if (hdcSeek == NULL) {\r
3136     hdcSeek = GetDC(hwndMain);\r
3137       if (!appData.monoMode) {\r
3138         SelectPalette(hdcSeek, hPal, FALSE);\r
3139         RealizePalette(hdcSeek);\r
3140       }\r
3141     }\r
3142     hp = SelectObject( hdcSeek, gridPen );\r
3143     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3144     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3145     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3146     SelectObject( hdcSeek, hp );\r
3147 }\r
3148 \r
3149 // front-end wrapper for putting text in graph\r
3150 void DrawSeekText(char *buf, int x, int y)\r
3151 {\r
3152         SIZE stSize;\r
3153         SetBkMode( hdcSeek, TRANSPARENT );\r
3154         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3155         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3156 }\r
3157 \r
3158 void DrawSeekDot(int x, int y, int color)\r
3159 {\r
3160         int square = color & 0x80;\r
3161         color &= 0x7F;\r
3162             HBRUSH oldBrush = SelectObject(hdcSeek, \r
3163                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3164         if(square)\r
3165             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3166                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3167         else\r
3168             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3169                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3170             SelectObject(hdcSeek, oldBrush);\r
3171 }\r
3172 \r
3173 VOID\r
3174 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3175 {\r
3176   static Board lastReq[2], lastDrawn[2];\r
3177   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3178   static int lastDrawnFlipView = 0;\r
3179   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3180   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3181   HDC tmphdc;\r
3182   HDC hdcmem;\r
3183   HBITMAP bufferBitmap;\r
3184   HBITMAP oldBitmap;\r
3185   RECT Rect;\r
3186   HRGN clips[MAX_CLIPS];\r
3187   ChessSquare dragged_piece = EmptySquare;\r
3188   int nr = twoBoards*partnerUp;\r
3189 \r
3190   /* I'm undecided on this - this function figures out whether a full\r
3191    * repaint is necessary on its own, so there's no real reason to have the\r
3192    * caller tell it that.  I think this can safely be set to FALSE - but\r
3193    * if we trust the callers not to request full repaints unnessesarily, then\r
3194    * we could skip some clipping work.  In other words, only request a full\r
3195    * redraw when the majority of pieces have changed positions (ie. flip, \r
3196    * gamestart and similar)  --Hawk\r
3197    */\r
3198   Boolean fullrepaint = repaint;\r
3199 \r
3200   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3201 \r
3202   if( DrawPositionNeedsFullRepaint() ) {\r
3203       fullrepaint = TRUE;\r
3204   }\r
3205 \r
3206   if (board == NULL) {\r
3207     if (!lastReqValid[nr]) {\r
3208       return;\r
3209     }\r
3210     board = lastReq[nr];\r
3211   } else {\r
3212     CopyBoard(lastReq[nr], board);\r
3213     lastReqValid[nr] = 1;\r
3214   }\r
3215 \r
3216   if (doingSizing) {\r
3217     return;\r
3218   }\r
3219 \r
3220   if (IsIconic(hwndMain)) {\r
3221     return;\r
3222   }\r
3223 \r
3224   if (hdc == NULL) {\r
3225     hdc = GetDC(hwndMain);\r
3226     if (!appData.monoMode) {\r
3227       SelectPalette(hdc, hPal, FALSE);\r
3228       RealizePalette(hdc);\r
3229     }\r
3230     releaseDC = TRUE;\r
3231   } else {\r
3232     releaseDC = FALSE;\r
3233   }\r
3234 \r
3235   /* Create some work-DCs */\r
3236   hdcmem = CreateCompatibleDC(hdc);\r
3237   tmphdc = CreateCompatibleDC(hdc);\r
3238 \r
3239   /* If dragging is in progress, we temporarely remove the piece */\r
3240   /* [HGM] or temporarily decrease count if stacked              */\r
3241   /*       !! Moved to before board compare !!                   */\r
3242   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3243     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3244     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3245             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3246         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3247     } else \r
3248     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3249             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3250         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3251     } else \r
3252         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3253   }\r
3254 \r
3255   /* Figure out which squares need updating by comparing the \r
3256    * newest board with the last drawn board and checking if\r
3257    * flipping has changed.\r
3258    */\r
3259   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3260     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3261       for (column = 0; column < BOARD_WIDTH; column++) {\r
3262         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3263           SquareToPos(row, column, &x, &y);\r
3264           clips[num_clips++] =\r
3265             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3266         }\r
3267       }\r
3268     }\r
3269    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3270     for (i=0; i<2; i++) {\r
3271       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3272           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3273         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3274             lastDrawnHighlight.sq[i].y >= 0) {\r
3275           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3276                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3277           clips[num_clips++] =\r
3278             CreateRectRgn(x - lineGap, y - lineGap, \r
3279                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3280         }\r
3281         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3282           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3283           clips[num_clips++] =\r
3284             CreateRectRgn(x - lineGap, y - lineGap, \r
3285                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3286         }\r
3287       }\r
3288     }\r
3289     for (i=0; i<2; i++) {\r
3290       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3291           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3292         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3293             lastDrawnPremove.sq[i].y >= 0) {\r
3294           SquareToPos(lastDrawnPremove.sq[i].y,\r
3295                       lastDrawnPremove.sq[i].x, &x, &y);\r
3296           clips[num_clips++] =\r
3297             CreateRectRgn(x - lineGap, y - lineGap, \r
3298                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3299         }\r
3300         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3301             premoveHighlightInfo.sq[i].y >= 0) {\r
3302           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3303                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3304           clips[num_clips++] =\r
3305             CreateRectRgn(x - lineGap, y - lineGap, \r
3306                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3307         }\r
3308       }\r
3309     }\r
3310    } else { // nr == 1\r
3311         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3312         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3313         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3314         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3315       for (i=0; i<2; i++) {\r
3316         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3317             partnerHighlightInfo.sq[i].y >= 0) {\r
3318           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3319                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3320           clips[num_clips++] =\r
3321             CreateRectRgn(x - lineGap, y - lineGap, \r
3322                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3323         }\r
3324         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3325             oldPartnerHighlight.sq[i].y >= 0) {\r
3326           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3327                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3328           clips[num_clips++] =\r
3329             CreateRectRgn(x - lineGap, y - lineGap, \r
3330                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3331         }\r
3332       }\r
3333    }\r
3334   } else {\r
3335     fullrepaint = TRUE;\r
3336   }\r
3337 \r
3338   /* Create a buffer bitmap - this is the actual bitmap\r
3339    * being written to.  When all the work is done, we can\r
3340    * copy it to the real DC (the screen).  This avoids\r
3341    * the problems with flickering.\r
3342    */\r
3343   GetClientRect(hwndMain, &Rect);\r
3344   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3345                                         Rect.bottom-Rect.top+1);\r
3346   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3347   if (!appData.monoMode) {\r
3348     SelectPalette(hdcmem, hPal, FALSE);\r
3349   }\r
3350 \r
3351   /* Create clips for dragging */\r
3352   if (!fullrepaint) {\r
3353     if (dragInfo.from.x >= 0) {\r
3354       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3355       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3356     }\r
3357     if (dragInfo.start.x >= 0) {\r
3358       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3359       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3360     }\r
3361     if (dragInfo.pos.x >= 0) {\r
3362       x = dragInfo.pos.x - squareSize / 2;\r
3363       y = dragInfo.pos.y - squareSize / 2;\r
3364       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3365     }\r
3366     if (dragInfo.lastpos.x >= 0) {\r
3367       x = dragInfo.lastpos.x - squareSize / 2;\r
3368       y = dragInfo.lastpos.y - squareSize / 2;\r
3369       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3370     }\r
3371   }\r
3372 \r
3373   /* Are we animating a move?  \r
3374    * If so, \r
3375    *   - remove the piece from the board (temporarely)\r
3376    *   - calculate the clipping region\r
3377    */\r
3378   if (!fullrepaint) {\r
3379     if (animInfo.piece != EmptySquare) {\r
3380       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3381       x = boardRect.left + animInfo.lastpos.x;\r
3382       y = boardRect.top + animInfo.lastpos.y;\r
3383       x2 = boardRect.left + animInfo.pos.x;\r
3384       y2 = boardRect.top + animInfo.pos.y;\r
3385       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3386       /* Slight kludge.  The real problem is that after AnimateMove is\r
3387          done, the position on the screen does not match lastDrawn.\r
3388          This currently causes trouble only on e.p. captures in\r
3389          atomic, where the piece moves to an empty square and then\r
3390          explodes.  The old and new positions both had an empty square\r
3391          at the destination, but animation has drawn a piece there and\r
3392          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3393       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3394     }\r
3395   }\r
3396 \r
3397   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3398   if (num_clips == 0)\r
3399     fullrepaint = TRUE;\r
3400 \r
3401   /* Set clipping on the memory DC */\r
3402   if (!fullrepaint) {\r
3403     SelectClipRgn(hdcmem, clips[0]);\r
3404     for (x = 1; x < num_clips; x++) {\r
3405       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3406         abort();  // this should never ever happen!\r
3407     }\r
3408   }\r
3409 \r
3410   /* Do all the drawing to the memory DC */\r
3411   if(explodeInfo.radius) { // [HGM] atomic\r
3412         HBRUSH oldBrush;\r
3413         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3414         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3415         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3416         x += squareSize/2;\r
3417         y += squareSize/2;\r
3418         if(!fullrepaint) {\r
3419           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3420           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3421         }\r
3422         DrawGridOnDC(hdcmem);\r
3423         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3424         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3425         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3426         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3427         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3428         SelectObject(hdcmem, oldBrush);\r
3429   } else {\r
3430     DrawGridOnDC(hdcmem);\r
3431     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3432         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3433         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3434     } else {\r
3435         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3436         oldPartnerHighlight = partnerHighlightInfo;\r
3437     }\r
3438     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3439   }\r
3440   if(nr == 0) // [HGM] dual: markers only on left board\r
3441   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3442     for (column = 0; column < BOARD_WIDTH; column++) {\r
3443         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3444             HBRUSH oldBrush = SelectObject(hdcmem, \r
3445                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3446             SquareToPos(row, column, &x, &y);\r
3447             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3448                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3449             SelectObject(hdcmem, oldBrush);\r
3450         }\r
3451     }\r
3452   }\r
3453   if(logoHeight) {\r
3454         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3455         if(appData.autoLogo) {\r
3456           \r
3457           switch(gameMode) { // pick logos based on game mode\r
3458             case IcsObserving:\r
3459                 whiteLogo = second.programLogo; // ICS logo\r
3460                 blackLogo = second.programLogo;\r
3461             default:\r
3462                 break;\r
3463             case IcsPlayingWhite:\r
3464                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3465                 blackLogo = second.programLogo; // ICS logo\r
3466                 break;\r
3467             case IcsPlayingBlack:\r
3468                 whiteLogo = second.programLogo; // ICS logo\r
3469                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3470                 break;\r
3471             case TwoMachinesPlay:\r
3472                 if(first.twoMachinesColor[0] == 'b') {\r
3473                     whiteLogo = second.programLogo;\r
3474                     blackLogo = first.programLogo;\r
3475                 }\r
3476                 break;\r
3477             case MachinePlaysWhite:\r
3478                 blackLogo = userLogo;\r
3479                 break;\r
3480             case MachinePlaysBlack:\r
3481                 whiteLogo = userLogo;\r
3482                 blackLogo = first.programLogo;\r
3483           }\r
3484         }\r
3485         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3486         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3487   }\r
3488 \r
3489   if( appData.highlightMoveWithArrow ) {\r
3490     DrawArrowHighlight(hdcmem);\r
3491   }\r
3492 \r
3493   DrawCoordsOnDC(hdcmem);\r
3494 \r
3495   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3496                  /* to make sure lastDrawn contains what is actually drawn */\r
3497 \r
3498   /* Put the dragged piece back into place and draw it (out of place!) */\r
3499     if (dragged_piece != EmptySquare) {\r
3500     /* [HGM] or restack */\r
3501     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3502                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3503     else\r
3504     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3505                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3506     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3507     x = dragInfo.pos.x - squareSize / 2;\r
3508     y = dragInfo.pos.y - squareSize / 2;\r
3509     DrawPieceOnDC(hdcmem, dragged_piece,\r
3510                   ((int) dragged_piece < (int) BlackPawn), \r
3511                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3512   }   \r
3513   \r
3514   /* Put the animated piece back into place and draw it */\r
3515   if (animInfo.piece != EmptySquare) {\r
3516     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3517     x = boardRect.left + animInfo.pos.x;\r
3518     y = boardRect.top + animInfo.pos.y;\r
3519     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3520                   ((int) animInfo.piece < (int) BlackPawn),\r
3521                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3522   }\r
3523 \r
3524   /* Release the bufferBitmap by selecting in the old bitmap \r
3525    * and delete the memory DC\r
3526    */\r
3527   SelectObject(hdcmem, oldBitmap);\r
3528   DeleteDC(hdcmem);\r
3529 \r
3530   /* Set clipping on the target DC */\r
3531   if (!fullrepaint) {\r
3532     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3533         RECT rect;\r
3534         GetRgnBox(clips[x], &rect);\r
3535         DeleteObject(clips[x]);\r
3536         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3537                           rect.right + wpMain.width/2, rect.bottom);\r
3538     }\r
3539     SelectClipRgn(hdc, clips[0]);\r
3540     for (x = 1; x < num_clips; x++) {\r
3541       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3542         abort();   // this should never ever happen!\r
3543     } \r
3544   }\r
3545 \r
3546   /* Copy the new bitmap onto the screen in one go.\r
3547    * This way we avoid any flickering\r
3548    */\r
3549   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3550   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3551          boardRect.right - boardRect.left,\r
3552          boardRect.bottom - boardRect.top,\r
3553          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3554   if(saveDiagFlag) { \r
3555     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3556     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3557 \r
3558     GetObject(bufferBitmap, sizeof(b), &b);\r
3559     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3560         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3561         bih.biWidth = b.bmWidth;\r
3562         bih.biHeight = b.bmHeight;\r
3563         bih.biPlanes = 1;\r
3564         bih.biBitCount = b.bmBitsPixel;\r
3565         bih.biCompression = 0;\r
3566         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3567         bih.biXPelsPerMeter = 0;\r
3568         bih.biYPelsPerMeter = 0;\r
3569         bih.biClrUsed = 0;\r
3570         bih.biClrImportant = 0;\r
3571 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3572 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3573         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3574 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3575 \r
3576         wb = b.bmWidthBytes;\r
3577         // count colors\r
3578         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3579                 int k = ((int*) pData)[i];\r
3580                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3581                 if(j >= 16) break;\r
3582                 color[j] = k;\r
3583                 if(j >= nrColors) nrColors = j+1;\r
3584         }\r
3585         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3586                 INT p = 0;\r
3587                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3588                     for(w=0; w<(wb>>2); w+=2) {\r
3589                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3590                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3591                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3592                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3593                         pData[p++] = m | j<<4;\r
3594                     }\r
3595                     while(p&3) pData[p++] = 0;\r
3596                 }\r
3597                 fac = 3;\r
3598                 wb = ((wb+31)>>5)<<2;\r
3599         }\r
3600         // write BITMAPFILEHEADER\r
3601         fprintf(diagFile, "BM");\r
3602         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3603         fputDW(diagFile, 0);\r
3604         fputDW(diagFile, 0x36 + (fac?64:0));\r
3605         // write BITMAPINFOHEADER\r
3606         fputDW(diagFile, 40);\r
3607         fputDW(diagFile, b.bmWidth);\r
3608         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3609         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3610         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3611         fputDW(diagFile, 0);\r
3612         fputDW(diagFile, 0);\r
3613         fputDW(diagFile, 0);\r
3614         fputDW(diagFile, 0);\r
3615         fputDW(diagFile, 0);\r
3616         fputDW(diagFile, 0);\r
3617         // write color table\r
3618         if(fac)\r
3619         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3620         // write bitmap data\r
3621         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3622                 fputc(pData[i], diagFile);\r
3623      }\r
3624   }\r
3625 \r
3626   SelectObject(tmphdc, oldBitmap);\r
3627 \r
3628   /* Massive cleanup */\r
3629   for (x = 0; x < num_clips; x++)\r
3630     DeleteObject(clips[x]);\r
3631 \r
3632   DeleteDC(tmphdc);\r
3633   DeleteObject(bufferBitmap);\r
3634 \r
3635   if (releaseDC) \r
3636     ReleaseDC(hwndMain, hdc);\r
3637   \r
3638   if (lastDrawnFlipView != flipView && nr == 0) {\r
3639     if (flipView)\r
3640       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3641     else\r
3642       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3643   }\r
3644 \r
3645 /*  CopyBoard(lastDrawn, board);*/\r
3646   lastDrawnHighlight = highlightInfo;\r
3647   lastDrawnPremove   = premoveHighlightInfo;\r
3648   lastDrawnFlipView = flipView;\r
3649   lastDrawnValid[nr] = 1;\r
3650 }\r
3651 \r
3652 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3653 int\r
3654 SaveDiagram(f)\r
3655      FILE *f;\r
3656 {\r
3657     saveDiagFlag = 1; diagFile = f;\r
3658     HDCDrawPosition(NULL, TRUE, NULL);\r
3659 \r
3660     saveDiagFlag = 0;\r
3661 \r
3662 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3663     \r
3664     fclose(f);\r
3665     return TRUE;\r
3666 }\r
3667 \r
3668 \r
3669 /*---------------------------------------------------------------------------*\\r
3670 | CLIENT PAINT PROCEDURE\r
3671 |   This is the main event-handler for the WM_PAINT message.\r
3672 |\r
3673 \*---------------------------------------------------------------------------*/\r
3674 VOID\r
3675 PaintProc(HWND hwnd)\r
3676 {\r
3677   HDC         hdc;\r
3678   PAINTSTRUCT ps;\r
3679   HFONT       oldFont;\r
3680 \r
3681   if((hdc = BeginPaint(hwnd, &ps))) {\r
3682     if (IsIconic(hwnd)) {\r
3683       DrawIcon(hdc, 2, 2, iconCurrent);\r
3684     } else {\r
3685       if (!appData.monoMode) {\r
3686         SelectPalette(hdc, hPal, FALSE);\r
3687         RealizePalette(hdc);\r
3688       }\r
3689       HDCDrawPosition(hdc, 1, NULL);\r
3690       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3691         flipView = !flipView; partnerUp = !partnerUp;\r
3692         HDCDrawPosition(hdc, 1, NULL);\r
3693         flipView = !flipView; partnerUp = !partnerUp;\r
3694       }\r
3695       oldFont =\r
3696         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3697       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3698                  ETO_CLIPPED|ETO_OPAQUE,\r
3699                  &messageRect, messageText, strlen(messageText), NULL);\r
3700       SelectObject(hdc, oldFont);\r
3701       DisplayBothClocks();\r
3702     }\r
3703     EndPaint(hwnd,&ps);\r
3704   }\r
3705 \r
3706   return;\r
3707 }\r
3708 \r
3709 \r
3710 /*\r
3711  * If the user selects on a border boundary, return -1; if off the board,\r
3712  *   return -2.  Otherwise map the event coordinate to the square.\r
3713  * The offset boardRect.left or boardRect.top must already have been\r
3714  *   subtracted from x.\r
3715  */\r
3716 int EventToSquare(x, limit)\r
3717      int x, limit;\r
3718 {\r
3719   if (x <= 0)\r
3720     return -2;\r
3721   if (x < lineGap)\r
3722     return -1;\r
3723   x -= lineGap;\r
3724   if ((x % (squareSize + lineGap)) >= squareSize)\r
3725     return -1;\r
3726   x /= (squareSize + lineGap);\r
3727     if (x >= limit)\r
3728     return -2;\r
3729   return x;\r
3730 }\r
3731 \r
3732 typedef struct {\r
3733   char piece;\r
3734   int command;\r
3735   char* name;\r
3736 } DropEnable;\r
3737 \r
3738 DropEnable dropEnables[] = {\r
3739   { 'P', DP_Pawn, "Pawn" },\r
3740   { 'N', DP_Knight, "Knight" },\r
3741   { 'B', DP_Bishop, "Bishop" },\r
3742   { 'R', DP_Rook, "Rook" },\r
3743   { 'Q', DP_Queen, "Queen" },\r
3744 };\r
3745 \r
3746 VOID\r
3747 SetupDropMenu(HMENU hmenu)\r
3748 {\r
3749   int i, count, enable;\r
3750   char *p;\r
3751   extern char white_holding[], black_holding[];\r
3752   char item[MSG_SIZ];\r
3753 \r
3754   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3755     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3756                dropEnables[i].piece);\r
3757     count = 0;\r
3758     while (p && *p++ == dropEnables[i].piece) count++;\r
3759     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
3760     enable = count > 0 || !appData.testLegality\r
3761       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3762                       && !appData.icsActive);\r
3763     ModifyMenu(hmenu, dropEnables[i].command,\r
3764                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3765                dropEnables[i].command, item);\r
3766   }\r
3767 }\r
3768 \r
3769 void DragPieceBegin(int x, int y)\r
3770 {\r
3771       dragInfo.lastpos.x = boardRect.left + x;\r
3772       dragInfo.lastpos.y = boardRect.top + y;\r
3773       dragInfo.from.x = fromX;\r
3774       dragInfo.from.y = fromY;\r
3775       dragInfo.start = dragInfo.from;\r
3776       SetCapture(hwndMain);\r
3777 }\r
3778 \r
3779 void DragPieceEnd(int x, int y)\r
3780 {\r
3781     ReleaseCapture();\r
3782     dragInfo.start.x = dragInfo.start.y = -1;\r
3783     dragInfo.from = dragInfo.start;\r
3784     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3785 }\r
3786 \r
3787 /* Event handler for mouse messages */\r
3788 VOID\r
3789 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3790 {\r
3791   int x, y, menuNr;\r
3792   POINT pt;\r
3793   static int recursive = 0;\r
3794   HMENU hmenu;\r
3795   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
3796 \r
3797   if (recursive) {\r
3798     if (message == WM_MBUTTONUP) {\r
3799       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
3800          to the middle button: we simulate pressing the left button too!\r
3801          */\r
3802       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
3803       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
3804     }\r
3805     return;\r
3806   }\r
3807   recursive++;\r
3808   \r
3809   pt.x = LOWORD(lParam);\r
3810   pt.y = HIWORD(lParam);\r
3811   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
3812   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
3813   if (!flipView && y >= 0) {\r
3814     y = BOARD_HEIGHT - 1 - y;\r
3815   }\r
3816   if (flipView && x >= 0) {\r
3817     x = BOARD_WIDTH - 1 - x;\r
3818   }\r
3819 \r
3820   switch (message) {\r
3821   case WM_LBUTTONDOWN:\r
3822       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3823         if (gameMode == EditPosition) {\r
3824           SetWhiteToPlayEvent();\r
3825         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
3826           AdjustClock(flipClock, -1);\r
3827         } else if (gameMode == IcsPlayingBlack ||\r
3828                    gameMode == MachinePlaysWhite) {\r
3829           CallFlagEvent();\r
3830         }\r
3831       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3832         if (gameMode == EditPosition) {\r
3833           SetBlackToPlayEvent();\r
3834         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
3835           AdjustClock(!flipClock, -1);\r
3836         } else if (gameMode == IcsPlayingWhite ||\r
3837                    gameMode == MachinePlaysBlack) {\r
3838           CallFlagEvent();\r
3839         }\r
3840       }\r
3841       dragInfo.start.x = dragInfo.start.y = -1;\r
3842       dragInfo.from = dragInfo.start;\r
3843     if(fromX == -1 && frozen) { // not sure where this is for\r
3844                 fromX = fromY = -1; \r
3845       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
3846       break;\r
3847     }\r
3848       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
3849       DrawPosition(TRUE, NULL);\r
3850     break;\r
3851 \r
3852   case WM_LBUTTONUP:\r
3853       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
3854       DrawPosition(TRUE, NULL);\r
3855     break;\r
3856 \r
3857   case WM_MOUSEMOVE:\r
3858     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
3859     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
3860     if ((appData.animateDragging || appData.highlightDragging)\r
3861         && (wParam & MK_LBUTTON)\r
3862         && dragInfo.from.x >= 0) \r
3863     {\r
3864       BOOL full_repaint = FALSE;\r
3865 \r
3866       if (appData.animateDragging) {\r
3867         dragInfo.pos = pt;\r
3868       }\r
3869       if (appData.highlightDragging) {\r
3870         SetHighlights(fromX, fromY, x, y);\r
3871         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
3872             full_repaint = TRUE;\r
3873         }\r
3874       }\r
3875       \r
3876       DrawPosition( full_repaint, NULL);\r
3877       \r
3878       dragInfo.lastpos = dragInfo.pos;\r
3879     }\r
3880     break;\r
3881 \r
3882   case WM_MOUSEWHEEL: // [DM]\r
3883     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
3884        /* Mouse Wheel is being rolled forward\r
3885         * Play moves forward\r
3886         */\r
3887        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
3888                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
3889        /* Mouse Wheel is being rolled backward\r
3890         * Play moves backward\r
3891         */\r
3892        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
3893                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
3894     }\r
3895     break;\r
3896 \r
3897   case WM_MBUTTONUP:\r
3898   case WM_RBUTTONUP:\r
3899     ReleaseCapture();\r
3900     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
3901     break;\r
3902  \r
3903   case WM_MBUTTONDOWN:\r
3904   case WM_RBUTTONDOWN:\r
3905     ErrorPopDown();\r
3906     ReleaseCapture();\r
3907     fromX = fromY = -1;\r
3908     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3909     dragInfo.start.x = dragInfo.start.y = -1;\r
3910     dragInfo.from = dragInfo.start;\r
3911     dragInfo.lastpos = dragInfo.pos;\r
3912     if (appData.highlightDragging) {\r
3913       ClearHighlights();\r
3914     }\r
3915     if(y == -2) {\r
3916       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
3917       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3918           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
3919       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3920           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
3921       }\r
3922       break;\r
3923     }\r
3924     DrawPosition(TRUE, NULL);\r
3925 \r
3926     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
3927     switch (menuNr) {\r
3928     case 0:\r
3929       if (message == WM_MBUTTONDOWN) {\r
3930         buttonCount = 3;  /* even if system didn't think so */\r
3931         if (wParam & MK_SHIFT) \r
3932           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3933         else\r
3934           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3935       } else { /* message == WM_RBUTTONDOWN */\r
3936         /* Just have one menu, on the right button.  Windows users don't\r
3937            think to try the middle one, and sometimes other software steals\r
3938            it, or it doesn't really exist. */\r
3939         if(gameInfo.variant != VariantShogi)\r
3940             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3941         else\r
3942             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
3943       }\r
3944       break;\r
3945     case 2:\r
3946       SetCapture(hwndMain);
3947       break;\r
3948     case 1:\r
3949       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3950       SetupDropMenu(hmenu);\r
3951       MenuPopup(hwnd, pt, hmenu, -1);\r
3952     default:\r
3953       break;\r
3954     }\r
3955     break;\r
3956   }\r
3957 \r
3958   recursive--;\r
3959 }\r
3960 \r
3961 /* Preprocess messages for buttons in main window */\r
3962 LRESULT CALLBACK\r
3963 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3964 {\r
3965   int id = GetWindowLong(hwnd, GWL_ID);\r
3966   int i, dir;\r
3967 \r
3968   for (i=0; i<N_BUTTONS; i++) {\r
3969     if (buttonDesc[i].id == id) break;\r
3970   }\r
3971   if (i == N_BUTTONS) return 0;\r
3972   switch (message) {\r
3973   case WM_KEYDOWN:\r
3974     switch (wParam) {\r
3975     case VK_LEFT:\r
3976     case VK_RIGHT:\r
3977       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3978       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3979       return TRUE;\r
3980     }\r
3981     break;\r
3982   case WM_CHAR:\r
3983     switch (wParam) {\r
3984     case '\r':\r
3985       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3986       return TRUE;\r
3987     default:\r
3988       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
3989         // [HGM] movenum: only letters or leading zero should go to ICS input\r
3990         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3991         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3992         SetFocus(h);\r
3993         SendMessage(h, WM_CHAR, wParam, lParam);\r
3994         return TRUE;\r
3995       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3996         PopUpMoveDialog((char)wParam);\r
3997       }\r
3998       break;\r
3999     }\r
4000     break;\r
4001   }\r
4002   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4003 }\r
4004 \r
4005 /* Process messages for Promotion dialog box */\r
4006 LRESULT CALLBACK\r
4007 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4008 {\r
4009   char promoChar;\r
4010 \r
4011   switch (message) {\r
4012   case WM_INITDIALOG: /* message: initialize dialog box */\r
4013     /* Center the dialog over the application window */\r
4014     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4015     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4016       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4017        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4018                SW_SHOW : SW_HIDE);\r
4019     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4020     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4021        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4022          PieceToChar(WhiteAngel) != '~') ||\r
4023         (PieceToChar(BlackAngel) >= 'A' &&\r
4024          PieceToChar(BlackAngel) != '~')   ) ?\r
4025                SW_SHOW : SW_HIDE);\r
4026     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4027        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4028          PieceToChar(WhiteMarshall) != '~') ||\r
4029         (PieceToChar(BlackMarshall) >= 'A' &&\r
4030          PieceToChar(BlackMarshall) != '~')   ) ?\r
4031                SW_SHOW : SW_HIDE);\r
4032     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4033     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4034        gameInfo.variant != VariantShogi ?\r
4035                SW_SHOW : SW_HIDE);\r
4036     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4037        gameInfo.variant != VariantShogi ?\r
4038                SW_SHOW : SW_HIDE);\r
4039     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4040        gameInfo.variant == VariantShogi ?\r
4041                SW_SHOW : SW_HIDE);\r
4042     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4043        gameInfo.variant == VariantShogi ?\r
4044                SW_SHOW : SW_HIDE);\r
4045     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4046        gameInfo.variant == VariantSuper ?\r
4047                SW_SHOW : SW_HIDE);\r
4048     return TRUE;\r
4049 \r
4050   case WM_COMMAND: /* message: received a command */\r
4051     switch (LOWORD(wParam)) {\r
4052     case IDCANCEL:\r
4053       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4054       ClearHighlights();\r
4055       DrawPosition(FALSE, NULL);\r
4056       return TRUE;\r
4057     case PB_King:\r
4058       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4059       break;\r
4060     case PB_Queen:\r
4061       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4062       break;\r
4063     case PB_Rook:\r
4064       promoChar = PieceToChar(BlackRook);\r
4065       break;\r
4066     case PB_Bishop:\r
4067       promoChar = PieceToChar(BlackBishop);\r
4068       break;\r
4069     case PB_Chancellor:\r
4070       promoChar = PieceToChar(BlackMarshall);\r
4071       break;\r
4072     case PB_Archbishop:\r
4073       promoChar = PieceToChar(BlackAngel);\r
4074       break;\r
4075     case PB_Knight:\r
4076       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4077       break;\r
4078     default:\r
4079       return FALSE;\r
4080     }\r
4081     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4082     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4083        only show the popup when we are already sure the move is valid or\r
4084        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4085        will figure out it is a promotion from the promoChar. */\r
4086     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4087     fromX = fromY = -1;\r
4088     if (!appData.highlightLastMove) {\r
4089       ClearHighlights();\r
4090       DrawPosition(FALSE, NULL);\r
4091     }\r
4092     return TRUE;\r
4093   }\r
4094   return FALSE;\r
4095 }\r
4096 \r
4097 /* Pop up promotion dialog */\r
4098 VOID\r
4099 PromotionPopup(HWND hwnd)\r
4100 {\r
4101   FARPROC lpProc;\r
4102 \r
4103   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4104   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4105     hwnd, (DLGPROC)lpProc);\r
4106   FreeProcInstance(lpProc);\r
4107 }\r
4108 \r
4109 void\r
4110 PromotionPopUp()\r
4111 {\r
4112   DrawPosition(TRUE, NULL);\r
4113   PromotionPopup(hwndMain);\r
4114 }\r
4115 \r
4116 /* Toggle ShowThinking */\r
4117 VOID\r
4118 ToggleShowThinking()\r
4119 {\r
4120   appData.showThinking = !appData.showThinking;\r
4121   ShowThinkingEvent();\r
4122 }\r
4123 \r
4124 VOID\r
4125 LoadGameDialog(HWND hwnd, char* title)\r
4126 {\r
4127   UINT number = 0;\r
4128   FILE *f;\r
4129   char fileTitle[MSG_SIZ];\r
4130   f = OpenFileDialog(hwnd, "rb", "",\r
4131                      appData.oldSaveStyle ? "gam" : "pgn",\r
4132                      GAME_FILT,\r
4133                      title, &number, fileTitle, NULL);\r
4134   if (f != NULL) {\r
4135     cmailMsgLoaded = FALSE;\r
4136     if (number == 0) {\r
4137       int error = GameListBuild(f);\r
4138       if (error) {\r
4139         DisplayError("Cannot build game list", error);\r
4140       } else if (!ListEmpty(&gameList) &&\r
4141                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4142         GameListPopUp(f, fileTitle);\r
4143         return;\r
4144       }\r
4145       GameListDestroy();\r
4146       number = 1;\r
4147     }\r
4148     LoadGame(f, number, fileTitle, FALSE);\r
4149   }\r
4150 }\r
4151 \r
4152 int get_term_width()\r
4153 {\r
4154     HDC hdc;\r
4155     TEXTMETRIC tm;\r
4156     RECT rc;\r
4157     HFONT hfont, hold_font;\r
4158     LOGFONT lf;\r
4159     HWND hText;\r
4160 \r
4161     if (hwndConsole)\r
4162         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4163     else\r
4164         return 79;\r
4165 \r
4166     // get the text metrics\r
4167     hdc = GetDC(hText);\r
4168     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4169     if (consoleCF.dwEffects & CFE_BOLD)\r
4170         lf.lfWeight = FW_BOLD;\r
4171     if (consoleCF.dwEffects & CFE_ITALIC)\r
4172         lf.lfItalic = TRUE;\r
4173     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4174         lf.lfStrikeOut = TRUE;\r
4175     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4176         lf.lfUnderline = TRUE;\r
4177     hfont = CreateFontIndirect(&lf);\r
4178     hold_font = SelectObject(hdc, hfont);\r
4179     GetTextMetrics(hdc, &tm);\r
4180     SelectObject(hdc, hold_font);\r
4181     DeleteObject(hfont);\r
4182     ReleaseDC(hText, hdc);\r
4183 \r
4184     // get the rectangle\r
4185     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4186 \r
4187     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4188 }\r
4189 \r
4190 void UpdateICSWidth(HWND hText)\r
4191 {\r
4192     LONG old_width, new_width;\r
4193 \r
4194     new_width = get_term_width(hText, FALSE);\r
4195     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4196     if (new_width != old_width)\r
4197     {\r
4198         ics_update_width(new_width);\r
4199         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4200     }\r
4201 }\r
4202 \r
4203 VOID\r
4204 ChangedConsoleFont()\r
4205 {\r
4206   CHARFORMAT cfmt;\r
4207   CHARRANGE tmpsel, sel;\r
4208   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4209   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4210   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4211   PARAFORMAT paraf;\r
4212 \r
4213   cfmt.cbSize = sizeof(CHARFORMAT);\r
4214   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4215   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4216   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4217    * size.  This was undocumented in the version of MSVC++ that I had\r
4218    * when I wrote the code, but is apparently documented now.\r
4219    */\r
4220   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4221   cfmt.bCharSet = f->lf.lfCharSet;\r
4222   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4223   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4224   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4225   /* Why are the following seemingly needed too? */\r
4226   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4227   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4228   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4229   tmpsel.cpMin = 0;\r
4230   tmpsel.cpMax = -1; /*999999?*/\r
4231   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4232   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4233   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4234    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4235    */\r
4236   paraf.cbSize = sizeof(paraf);\r
4237   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4238   paraf.dxStartIndent = 0;\r
4239   paraf.dxOffset = WRAP_INDENT;\r
4240   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4241   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4242   UpdateICSWidth(hText);\r
4243 }\r
4244 \r
4245 /*---------------------------------------------------------------------------*\\r
4246  *\r
4247  * Window Proc for main window\r
4248  *\r
4249 \*---------------------------------------------------------------------------*/\r
4250 \r
4251 /* Process messages for main window, etc. */\r
4252 LRESULT CALLBACK\r
4253 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4254 {\r
4255   FARPROC lpProc;\r
4256   int wmId, wmEvent;\r
4257   char *defName;\r
4258   FILE *f;\r
4259   UINT number;\r
4260   char fileTitle[MSG_SIZ];\r
4261   char buf[MSG_SIZ];\r
4262   static SnapData sd;\r
4263 \r
4264   switch (message) {\r
4265 \r
4266   case WM_PAINT: /* message: repaint portion of window */\r
4267     PaintProc(hwnd);\r
4268     break;\r
4269 \r
4270   case WM_ERASEBKGND:\r
4271     if (IsIconic(hwnd)) {\r
4272       /* Cheat; change the message */\r
4273       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4274     } else {\r
4275       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4276     }\r
4277     break;\r
4278 \r
4279   case WM_LBUTTONDOWN:\r
4280   case WM_MBUTTONDOWN:\r
4281   case WM_RBUTTONDOWN:\r
4282   case WM_LBUTTONUP:\r
4283   case WM_MBUTTONUP:\r
4284   case WM_RBUTTONUP:\r
4285   case WM_MOUSEMOVE:\r
4286   case WM_MOUSEWHEEL:\r
4287     MouseEvent(hwnd, message, wParam, lParam);\r
4288     break;\r
4289 \r
4290   JAWS_KB_NAVIGATION\r
4291 \r
4292   case WM_CHAR:\r
4293     \r
4294     JAWS_ALT_INTERCEPT\r
4295 \r
4296     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4297         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4298         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4299         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4300         SetFocus(h);\r
4301         SendMessage(h, message, wParam, lParam);\r
4302     } else if(lParam != KF_REPEAT) {\r
4303         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4304                 PopUpMoveDialog((char)wParam);\r
4305         } else if((char)wParam == 003) CopyGameToClipboard();\r
4306          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4307     }\r
4308 \r
4309     break;\r
4310 \r
4311   case WM_PALETTECHANGED:\r
4312     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4313       int nnew;\r
4314       HDC hdc = GetDC(hwndMain);\r
4315       SelectPalette(hdc, hPal, TRUE);\r
4316       nnew = RealizePalette(hdc);\r
4317       if (nnew > 0) {\r
4318         paletteChanged = TRUE;\r
4319         InvalidateRect(hwnd, &boardRect, FALSE);\r
4320       }\r
4321       ReleaseDC(hwnd, hdc);\r
4322     }\r
4323     break;\r
4324 \r
4325   case WM_QUERYNEWPALETTE:\r
4326     if (!appData.monoMode /*&& paletteChanged*/) {\r
4327       int nnew;\r
4328       HDC hdc = GetDC(hwndMain);\r
4329       paletteChanged = FALSE;\r
4330       SelectPalette(hdc, hPal, FALSE);\r
4331       nnew = RealizePalette(hdc);\r
4332       if (nnew > 0) {\r
4333         InvalidateRect(hwnd, &boardRect, FALSE);\r
4334       }\r
4335       ReleaseDC(hwnd, hdc);\r
4336       return TRUE;\r
4337     }\r
4338     return FALSE;\r
4339 \r
4340   case WM_COMMAND: /* message: command from application menu */\r
4341     wmId    = LOWORD(wParam);\r
4342     wmEvent = HIWORD(wParam);\r
4343 \r
4344     switch (wmId) {\r
4345     case IDM_NewGame:\r
4346       ResetGameEvent();\r
4347       SAY("new game enter a move to play against the computer with white");\r
4348       break;\r
4349 \r
4350     case IDM_NewGameFRC:\r
4351       if( NewGameFRC() == 0 ) {\r
4352         ResetGameEvent();\r
4353       }\r
4354       break;\r
4355 \r
4356     case IDM_NewVariant:\r
4357       NewVariantPopup(hwnd);\r
4358       break;\r
4359 \r
4360     case IDM_LoadGame:\r
4361       LoadGameDialog(hwnd, "Load Game from File");\r
4362       break;\r
4363 \r
4364     case IDM_LoadNextGame:\r
4365       ReloadGame(1);\r
4366       break;\r
4367 \r
4368     case IDM_LoadPrevGame:\r
4369       ReloadGame(-1);\r
4370       break;\r
4371 \r
4372     case IDM_ReloadGame:\r
4373       ReloadGame(0);\r
4374       break;\r
4375 \r
4376     case IDM_LoadPosition:\r
4377       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4378         Reset(FALSE, TRUE);\r
4379       }\r
4380       number = 1;\r
4381       f = OpenFileDialog(hwnd, "rb", "",\r
4382                          appData.oldSaveStyle ? "pos" : "fen",\r
4383                          POSITION_FILT,\r
4384                          "Load Position from File", &number, fileTitle, NULL);\r
4385       if (f != NULL) {\r
4386         LoadPosition(f, number, fileTitle);\r
4387       }\r
4388       break;\r
4389 \r
4390     case IDM_LoadNextPosition:\r
4391       ReloadPosition(1);\r
4392       break;\r
4393 \r
4394     case IDM_LoadPrevPosition:\r
4395       ReloadPosition(-1);\r
4396       break;\r
4397 \r
4398     case IDM_ReloadPosition:\r
4399       ReloadPosition(0);\r
4400       break;\r
4401 \r
4402     case IDM_SaveGame:\r
4403       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4404       f = OpenFileDialog(hwnd, "a", defName,\r
4405                          appData.oldSaveStyle ? "gam" : "pgn",\r
4406                          GAME_FILT,\r
4407                          "Save Game to File", NULL, fileTitle, NULL);\r
4408       if (f != NULL) {\r
4409         SaveGame(f, 0, "");\r
4410       }\r
4411       break;\r
4412 \r
4413     case IDM_SavePosition:\r
4414       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4415       f = OpenFileDialog(hwnd, "a", defName,\r
4416                          appData.oldSaveStyle ? "pos" : "fen",\r
4417                          POSITION_FILT,\r
4418                          "Save Position to File", NULL, fileTitle, NULL);\r
4419       if (f != NULL) {\r
4420         SavePosition(f, 0, "");\r
4421       }\r
4422       break;\r
4423 \r
4424     case IDM_SaveDiagram:\r
4425       defName = "diagram";\r
4426       f = OpenFileDialog(hwnd, "wb", defName,\r
4427                          "bmp",\r
4428                          DIAGRAM_FILT,\r
4429                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4430       if (f != NULL) {\r
4431         SaveDiagram(f);\r
4432       }\r
4433       break;\r
4434 \r
4435     case IDM_CopyGame:\r
4436       CopyGameToClipboard();\r
4437       break;\r
4438 \r
4439     case IDM_PasteGame:\r
4440       PasteGameFromClipboard();\r
4441       break;\r
4442 \r
4443     case IDM_CopyGameListToClipboard:\r
4444       CopyGameListToClipboard();\r
4445       break;\r
4446 \r
4447     /* [AS] Autodetect FEN or PGN data */\r
4448     case IDM_PasteAny:\r
4449       PasteGameOrFENFromClipboard();\r
4450       break;\r
4451 \r
4452     /* [AS] Move history */\r
4453     case IDM_ShowMoveHistory:\r
4454         if( MoveHistoryIsUp() ) {\r
4455             MoveHistoryPopDown();\r
4456         }\r
4457         else {\r
4458             MoveHistoryPopUp();\r
4459         }\r
4460         break;\r
4461 \r
4462     /* [AS] Eval graph */\r
4463     case IDM_ShowEvalGraph:\r
4464         if( EvalGraphIsUp() ) {\r
4465             EvalGraphPopDown();\r
4466         }\r
4467         else {\r
4468             EvalGraphPopUp();\r
4469             SetFocus(hwndMain);\r
4470         }\r
4471         break;\r
4472 \r
4473     /* [AS] Engine output */\r
4474     case IDM_ShowEngineOutput:\r
4475         if( EngineOutputIsUp() ) {\r
4476             EngineOutputPopDown();\r
4477         }\r
4478         else {\r
4479             EngineOutputPopUp();\r
4480         }\r
4481         break;\r
4482 \r
4483     /* [AS] User adjudication */\r
4484     case IDM_UserAdjudication_White:\r
4485         UserAdjudicationEvent( +1 );\r
4486         break;\r
4487 \r
4488     case IDM_UserAdjudication_Black:\r
4489         UserAdjudicationEvent( -1 );\r
4490         break;\r
4491 \r
4492     case IDM_UserAdjudication_Draw:\r
4493         UserAdjudicationEvent( 0 );\r
4494         break;\r
4495 \r
4496     /* [AS] Game list options dialog */\r
4497     case IDM_GameListOptions:\r
4498       GameListOptions();\r
4499       break;\r
4500 \r
4501     case IDM_NewChat:\r
4502       ChatPopUp(NULL);\r
4503       break;\r
4504 \r
4505     case IDM_CopyPosition:\r
4506       CopyFENToClipboard();\r
4507       break;\r
4508 \r
4509     case IDM_PastePosition:\r
4510       PasteFENFromClipboard();\r
4511       break;\r
4512 \r
4513     case IDM_MailMove:\r
4514       MailMoveEvent();\r
4515       break;\r
4516 \r
4517     case IDM_ReloadCMailMsg:\r
4518       Reset(TRUE, TRUE);\r
4519       ReloadCmailMsgEvent(FALSE);\r
4520       break;\r
4521 \r
4522     case IDM_Minimize:\r
4523       ShowWindow(hwnd, SW_MINIMIZE);\r
4524       break;\r
4525 \r
4526     case IDM_Exit:\r
4527       ExitEvent(0);\r
4528       break;\r
4529 \r
4530     case IDM_MachineWhite:\r
4531       MachineWhiteEvent();\r
4532       /*\r
4533        * refresh the tags dialog only if it's visible\r
4534        */\r
4535       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4536           char *tags;\r
4537           tags = PGNTags(&gameInfo);\r
4538           TagsPopUp(tags, CmailMsg());\r
4539           free(tags);\r
4540       }\r
4541       SAY("computer starts playing white");\r
4542       break;\r
4543 \r
4544     case IDM_MachineBlack:\r
4545       MachineBlackEvent();\r
4546       /*\r
4547        * refresh the tags dialog only if it's visible\r
4548        */\r
4549       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4550           char *tags;\r
4551           tags = PGNTags(&gameInfo);\r
4552           TagsPopUp(tags, CmailMsg());\r
4553           free(tags);\r
4554       }\r
4555       SAY("computer starts playing black");\r
4556       break;\r
4557 \r
4558     case IDM_TwoMachines:\r
4559       TwoMachinesEvent();\r
4560       /*\r
4561        * refresh the tags dialog only if it's visible\r
4562        */\r
4563       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4564           char *tags;\r
4565           tags = PGNTags(&gameInfo);\r
4566           TagsPopUp(tags, CmailMsg());\r
4567           free(tags);\r
4568       }\r
4569       SAY("computer starts playing both sides");\r
4570       break;\r
4571 \r
4572     case IDM_AnalysisMode:\r
4573       if (!first.analysisSupport) {\r
4574         sprintf(buf, "%s does not support analysis", first.tidy);\r
4575         DisplayError(buf, 0);\r
4576       } else {\r
4577         SAY("analyzing current position");\r
4578         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4579         if (appData.icsActive) {\r
4580                if (gameMode != IcsObserving) {\r
4581                        sprintf(buf, "You are not observing a game");\r
4582                        DisplayError(buf, 0);\r
4583                        /* secure check */\r
4584                        if (appData.icsEngineAnalyze) {\r
4585                                if (appData.debugMode) \r
4586                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4587                                ExitAnalyzeMode();\r
4588                                ModeHighlight();\r
4589                                break;\r
4590                        }\r
4591                        break;\r
4592                } else {\r
4593                        /* if enable, user want disable icsEngineAnalyze */\r
4594                        if (appData.icsEngineAnalyze) {\r
4595                                ExitAnalyzeMode();\r
4596                                ModeHighlight();\r
4597                                break;\r
4598                        }\r
4599                        appData.icsEngineAnalyze = TRUE;\r
4600                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4601                }\r
4602         } \r
4603         if (!appData.showThinking) ToggleShowThinking();\r
4604         AnalyzeModeEvent();\r
4605       }\r
4606       break;\r
4607 \r
4608     case IDM_AnalyzeFile:\r
4609       if (!first.analysisSupport) {\r
4610         char buf[MSG_SIZ];\r
4611         sprintf(buf, "%s does not support analysis", first.tidy);\r
4612         DisplayError(buf, 0);\r
4613       } else {\r
4614         if (!appData.showThinking) ToggleShowThinking();\r
4615         AnalyzeFileEvent();\r
4616         LoadGameDialog(hwnd, "Analyze Game from File");\r
4617         AnalysisPeriodicEvent(1);\r
4618       }\r
4619       break;\r
4620 \r
4621     case IDM_IcsClient:\r
4622       IcsClientEvent();\r
4623       break;\r
4624 \r
4625     case IDM_EditGame:\r
4626       EditGameEvent();\r
4627       SAY("edit game");\r
4628       break;\r
4629 \r
4630     case IDM_EditPosition:\r
4631       EditPositionEvent();\r
4632       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4633       break;\r
4634 \r
4635     case IDM_Training:\r
4636       TrainingEvent();\r
4637       break;\r
4638 \r
4639     case IDM_ShowGameList:\r
4640       ShowGameListProc();\r
4641       break;\r
4642 \r
4643     case IDM_EditTags:\r
4644       EditTagsProc();\r
4645       break;\r
4646 \r
4647     case IDM_EditComment:\r
4648       if (commentUp && editComment) {\r
4649         CommentPopDown();\r
4650       } else {\r
4651         EditCommentEvent();\r
4652       }\r
4653       break;\r
4654 \r
4655     case IDM_Pause:\r
4656       PauseEvent();\r
4657       break;\r
4658 \r
4659     case IDM_Accept:\r
4660       AcceptEvent();\r
4661       break;\r
4662 \r
4663     case IDM_Decline:\r
4664       DeclineEvent();\r
4665       break;\r
4666 \r
4667     case IDM_Rematch:\r
4668       RematchEvent();\r
4669       break;\r
4670 \r
4671     case IDM_CallFlag:\r
4672       CallFlagEvent();\r
4673       break;\r
4674 \r
4675     case IDM_Draw:\r
4676       DrawEvent();\r
4677       break;\r
4678 \r
4679     case IDM_Adjourn:\r
4680       AdjournEvent();\r
4681       break;\r
4682 \r
4683     case IDM_Abort:\r
4684       AbortEvent();\r
4685       break;\r
4686 \r
4687     case IDM_Resign:\r
4688       ResignEvent();\r
4689       break;\r
4690 \r
4691     case IDM_StopObserving:\r
4692       StopObservingEvent();\r
4693       break;\r
4694 \r
4695     case IDM_StopExamining:\r
4696       StopExaminingEvent();\r
4697       break;\r
4698 \r
4699     case IDM_Upload:\r
4700       UploadGameEvent();\r
4701       break;\r
4702 \r
4703     case IDM_TypeInMove:\r
4704       PopUpMoveDialog('\000');\r
4705       break;\r
4706 \r
4707     case IDM_TypeInName:\r
4708       PopUpNameDialog('\000');\r
4709       break;\r
4710 \r
4711     case IDM_Backward:\r
4712       BackwardEvent();\r
4713       SetFocus(hwndMain);\r
4714       break;\r
4715 \r
4716     JAWS_MENU_ITEMS\r
4717 \r
4718     case IDM_Forward:\r
4719       ForwardEvent();\r
4720       SetFocus(hwndMain);\r
4721       break;\r
4722 \r
4723     case IDM_ToStart:\r
4724       ToStartEvent();\r
4725       SetFocus(hwndMain);\r
4726       break;\r
4727 \r
4728     case IDM_ToEnd:\r
4729       ToEndEvent();\r
4730       SetFocus(hwndMain);\r
4731       break;\r
4732 \r
4733     case IDM_Revert:\r
4734       RevertEvent(FALSE);\r
4735       break;\r
4736 \r
4737     case IDM_Annotate: // [HGM] vari: revert with annotation\r
4738       RevertEvent(TRUE);\r
4739       break;\r
4740 \r
4741     case IDM_TruncateGame:\r
4742       TruncateGameEvent();\r
4743       break;\r
4744 \r
4745     case IDM_MoveNow:\r
4746       MoveNowEvent();\r
4747       break;\r
4748 \r
4749     case IDM_RetractMove:\r
4750       RetractMoveEvent();\r
4751       break;\r
4752 \r
4753     case IDM_FlipView:\r
4754       flipView = !flipView;\r
4755       DrawPosition(FALSE, NULL);\r
4756       break;\r
4757 \r
4758     case IDM_FlipClock:\r
4759       flipClock = !flipClock;\r
4760       DisplayBothClocks();\r
4761       DrawPosition(FALSE, NULL);\r
4762       break;\r
4763 \r
4764     case IDM_MuteSounds:\r
4765       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4766       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4767                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4768       break;\r
4769 \r
4770     case IDM_GeneralOptions:\r
4771       GeneralOptionsPopup(hwnd);\r
4772       DrawPosition(TRUE, NULL);\r
4773       break;\r
4774 \r
4775     case IDM_BoardOptions:\r
4776       BoardOptionsPopup(hwnd);\r
4777       break;\r
4778 \r
4779     case IDM_EnginePlayOptions:\r
4780       EnginePlayOptionsPopup(hwnd);\r
4781       break;\r
4782 \r
4783     case IDM_Engine1Options:\r
4784       EngineOptionsPopup(hwnd, &first);\r
4785       break;\r
4786 \r
4787     case IDM_Engine2Options:\r
4788       EngineOptionsPopup(hwnd, &second);\r
4789       break;\r
4790 \r
4791     case IDM_OptionsUCI:\r
4792       UciOptionsPopup(hwnd);\r
4793       break;\r
4794 \r
4795     case IDM_IcsOptions:\r
4796       IcsOptionsPopup(hwnd);\r
4797       break;\r
4798 \r
4799     case IDM_Fonts:\r
4800       FontsOptionsPopup(hwnd);\r
4801       break;\r
4802 \r
4803     case IDM_Sounds:\r
4804       SoundOptionsPopup(hwnd);\r
4805       break;\r
4806 \r
4807     case IDM_CommPort:\r
4808       CommPortOptionsPopup(hwnd);\r
4809       break;\r
4810 \r
4811     case IDM_LoadOptions:\r
4812       LoadOptionsPopup(hwnd);\r
4813       break;\r
4814 \r
4815     case IDM_SaveOptions:\r
4816       SaveOptionsPopup(hwnd);\r
4817       break;\r
4818 \r
4819     case IDM_TimeControl:\r
4820       TimeControlOptionsPopup(hwnd);\r
4821       break;\r
4822 \r
4823     case IDM_SaveSettings:\r
4824       SaveSettings(settingsFileName);\r
4825       break;\r
4826 \r
4827     case IDM_SaveSettingsOnExit:\r
4828       saveSettingsOnExit = !saveSettingsOnExit;\r
4829       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4830                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4831                                          MF_CHECKED : MF_UNCHECKED));\r
4832       break;\r
4833 \r
4834     case IDM_Hint:\r
4835       HintEvent();\r
4836       break;\r
4837 \r
4838     case IDM_Book:\r
4839       BookEvent();\r
4840       break;\r
4841 \r
4842     case IDM_AboutGame:\r
4843       AboutGameEvent();\r
4844       break;\r
4845 \r
4846     case IDM_Debug:\r
4847       appData.debugMode = !appData.debugMode;\r
4848       if (appData.debugMode) {\r
4849         char dir[MSG_SIZ];\r
4850         GetCurrentDirectory(MSG_SIZ, dir);\r
4851         SetCurrentDirectory(installDir);\r
4852         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4853         SetCurrentDirectory(dir);\r
4854         setbuf(debugFP, NULL);\r
4855       } else {\r
4856         fclose(debugFP);\r
4857         debugFP = NULL;\r
4858       }\r
4859       break;\r
4860 \r
4861     case IDM_HELPCONTENTS:\r
4862       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4863           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4864           MessageBox (GetFocus(),\r
4865                     "Unable to activate help",\r
4866                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4867       }\r
4868       break;\r
4869 \r
4870     case IDM_HELPSEARCH:\r
4871         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4872             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4873         MessageBox (GetFocus(),\r
4874                     "Unable to activate help",\r
4875                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4876       }\r
4877       break;\r
4878 \r
4879     case IDM_HELPHELP:\r
4880       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4881         MessageBox (GetFocus(),\r
4882                     "Unable to activate help",\r
4883                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4884       }\r
4885       break;\r
4886 \r
4887     case IDM_ABOUT:\r
4888       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4889       DialogBox(hInst, \r
4890         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4891         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4892       FreeProcInstance(lpProc);\r
4893       break;\r
4894 \r
4895     case IDM_DirectCommand1:\r
4896       AskQuestionEvent("Direct Command",\r
4897                        "Send to chess program:", "", "1");\r
4898       break;\r
4899     case IDM_DirectCommand2:\r
4900       AskQuestionEvent("Direct Command",\r
4901                        "Send to second chess program:", "", "2");\r
4902       break;\r
4903 \r
4904     case EP_WhitePawn:\r
4905       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4906       fromX = fromY = -1;\r
4907       break;\r
4908 \r
4909     case EP_WhiteKnight:\r
4910       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4911       fromX = fromY = -1;\r
4912       break;\r
4913 \r
4914     case EP_WhiteBishop:\r
4915       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4916       fromX = fromY = -1;\r
4917       break;\r
4918 \r
4919     case EP_WhiteRook:\r
4920       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4921       fromX = fromY = -1;\r
4922       break;\r
4923 \r
4924     case EP_WhiteQueen:\r
4925       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4926       fromX = fromY = -1;\r
4927       break;\r
4928 \r
4929     case EP_WhiteFerz:\r
4930       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4931       fromX = fromY = -1;\r
4932       break;\r
4933 \r
4934     case EP_WhiteWazir:\r
4935       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4936       fromX = fromY = -1;\r
4937       break;\r
4938 \r
4939     case EP_WhiteAlfil:\r
4940       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4941       fromX = fromY = -1;\r
4942       break;\r
4943 \r
4944     case EP_WhiteCannon:\r
4945       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4946       fromX = fromY = -1;\r
4947       break;\r
4948 \r
4949     case EP_WhiteCardinal:\r
4950       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4951       fromX = fromY = -1;\r
4952       break;\r
4953 \r
4954     case EP_WhiteMarshall:\r
4955       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4956       fromX = fromY = -1;\r
4957       break;\r
4958 \r
4959     case EP_WhiteKing:\r
4960       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4961       fromX = fromY = -1;\r
4962       break;\r
4963 \r
4964     case EP_BlackPawn:\r
4965       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4966       fromX = fromY = -1;\r
4967       break;\r
4968 \r
4969     case EP_BlackKnight:\r
4970       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4971       fromX = fromY = -1;\r
4972       break;\r
4973 \r
4974     case EP_BlackBishop:\r
4975       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4976       fromX = fromY = -1;\r
4977       break;\r
4978 \r
4979     case EP_BlackRook:\r
4980       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4981       fromX = fromY = -1;\r
4982       break;\r
4983 \r
4984     case EP_BlackQueen:\r
4985       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4986       fromX = fromY = -1;\r
4987       break;\r
4988 \r
4989     case EP_BlackFerz:\r
4990       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4991       fromX = fromY = -1;\r
4992       break;\r
4993 \r
4994     case EP_BlackWazir:\r
4995       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
4996       fromX = fromY = -1;\r
4997       break;\r
4998 \r
4999     case EP_BlackAlfil:\r
5000       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5001       fromX = fromY = -1;\r
5002       break;\r
5003 \r
5004     case EP_BlackCannon:\r
5005       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5006       fromX = fromY = -1;\r
5007       break;\r
5008 \r
5009     case EP_BlackCardinal:\r
5010       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5011       fromX = fromY = -1;\r
5012       break;\r
5013 \r
5014     case EP_BlackMarshall:\r
5015       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5016       fromX = fromY = -1;\r
5017       break;\r
5018 \r
5019     case EP_BlackKing:\r
5020       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5021       fromX = fromY = -1;\r
5022       break;\r
5023 \r
5024     case EP_EmptySquare:\r
5025       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5026       fromX = fromY = -1;\r
5027       break;\r
5028 \r
5029     case EP_ClearBoard:\r
5030       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5031       fromX = fromY = -1;\r
5032       break;\r
5033 \r
5034     case EP_White:\r
5035       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5036       fromX = fromY = -1;\r
5037       break;\r
5038 \r
5039     case EP_Black:\r
5040       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5041       fromX = fromY = -1;\r
5042       break;\r
5043 \r
5044     case EP_Promote:\r
5045       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5046       fromX = fromY = -1;\r
5047       break;\r
5048 \r
5049     case EP_Demote:\r
5050       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5051       fromX = fromY = -1;\r
5052       break;\r
5053 \r
5054     case DP_Pawn:\r
5055       DropMenuEvent(WhitePawn, fromX, fromY);\r
5056       fromX = fromY = -1;\r
5057       break;\r
5058 \r
5059     case DP_Knight:\r
5060       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5061       fromX = fromY = -1;\r
5062       break;\r
5063 \r
5064     case DP_Bishop:\r
5065       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5066       fromX = fromY = -1;\r
5067       break;\r
5068 \r
5069     case DP_Rook:\r
5070       DropMenuEvent(WhiteRook, fromX, fromY);\r
5071       fromX = fromY = -1;\r
5072       break;\r
5073 \r
5074     case DP_Queen:\r
5075       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5076       fromX = fromY = -1;\r
5077       break;\r
5078 \r
5079     default:\r
5080       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5081     }\r
5082     break;\r
5083 \r
5084   case WM_TIMER:\r
5085     switch (wParam) {\r
5086     case CLOCK_TIMER_ID:\r
5087       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5088       clockTimerEvent = 0;\r
5089       DecrementClocks(); /* call into back end */\r
5090       break;\r
5091     case LOAD_GAME_TIMER_ID:\r
5092       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5093       loadGameTimerEvent = 0;\r
5094       AutoPlayGameLoop(); /* call into back end */\r
5095       break;\r
5096     case ANALYSIS_TIMER_ID:\r
5097       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5098                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5099         AnalysisPeriodicEvent(0);\r
5100       } else {\r
5101         KillTimer(hwnd, analysisTimerEvent);\r
5102         analysisTimerEvent = 0;\r
5103       }\r
5104       break;\r
5105     case DELAYED_TIMER_ID:\r
5106       KillTimer(hwnd, delayedTimerEvent);\r
5107       delayedTimerEvent = 0;\r
5108       delayedTimerCallback();\r
5109       break;\r
5110     }\r
5111     break;\r
5112 \r
5113   case WM_USER_Input:\r
5114     InputEvent(hwnd, message, wParam, lParam);\r
5115     break;\r
5116 \r
5117   /* [AS] Also move "attached" child windows */\r
5118   case WM_WINDOWPOSCHANGING:\r
5119 \r
5120     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5121         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5122 \r
5123         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5124             /* Window is moving */\r
5125             RECT rcMain;\r
5126 \r
5127 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5128             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5129             rcMain.right  = wpMain.x + wpMain.width;\r
5130             rcMain.top    = wpMain.y;\r
5131             rcMain.bottom = wpMain.y + wpMain.height;\r
5132             \r
5133             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5134             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5135             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5136             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5137             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5138             wpMain.x = lpwp->x;\r
5139             wpMain.y = lpwp->y;\r
5140         }\r
5141     }\r
5142     break;\r
5143 \r
5144   /* [AS] Snapping */\r
5145   case WM_ENTERSIZEMOVE:\r
5146     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5147     if (hwnd == hwndMain) {\r
5148       doingSizing = TRUE;\r
5149       lastSizing = 0;\r
5150     }\r
5151     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5152     break;\r
5153 \r
5154   case WM_SIZING:\r
5155     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5156     if (hwnd == hwndMain) {\r
5157       lastSizing = wParam;\r
5158     }\r
5159     break;\r
5160 \r
5161   case WM_MOVING:\r
5162     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5163       return OnMoving( &sd, hwnd, wParam, lParam );\r
5164 \r
5165   case WM_EXITSIZEMOVE:\r
5166     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5167     if (hwnd == hwndMain) {\r
5168       RECT client;\r
5169       doingSizing = FALSE;\r
5170       InvalidateRect(hwnd, &boardRect, FALSE);\r
5171       GetClientRect(hwnd, &client);\r
5172       ResizeBoard(client.right, client.bottom, lastSizing);\r
5173       lastSizing = 0;\r
5174       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5175     }\r
5176     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5177     break;\r
5178 \r
5179   case WM_DESTROY: /* message: window being destroyed */\r
5180     PostQuitMessage(0);\r
5181     break;\r
5182 \r
5183   case WM_CLOSE:\r
5184     if (hwnd == hwndMain) {\r
5185       ExitEvent(0);\r
5186     }\r
5187     break;\r
5188 \r
5189   default:      /* Passes it on if unprocessed */\r
5190     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5191   }\r
5192   return 0;\r
5193 }\r
5194 \r
5195 /*---------------------------------------------------------------------------*\\r
5196  *\r
5197  * Misc utility routines\r
5198  *\r
5199 \*---------------------------------------------------------------------------*/\r
5200 \r
5201 /*\r
5202  * Decent random number generator, at least not as bad as Windows\r
5203  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5204  */\r
5205 unsigned int randstate;\r
5206 \r
5207 int\r
5208 myrandom(void)\r
5209 {\r
5210   randstate = randstate * 1664525 + 1013904223;\r
5211   return (int) randstate & 0x7fffffff;\r
5212 }\r
5213 \r
5214 void\r
5215 mysrandom(unsigned int seed)\r
5216 {\r
5217   randstate = seed;\r
5218 }\r
5219 \r
5220 \r
5221 /* \r
5222  * returns TRUE if user selects a different color, FALSE otherwise \r
5223  */\r
5224 \r
5225 BOOL\r
5226 ChangeColor(HWND hwnd, COLORREF *which)\r
5227 {\r
5228   static BOOL firstTime = TRUE;\r
5229   static DWORD customColors[16];\r
5230   CHOOSECOLOR cc;\r
5231   COLORREF newcolor;\r
5232   int i;\r
5233   ColorClass ccl;\r
5234 \r
5235   if (firstTime) {\r
5236     /* Make initial colors in use available as custom colors */\r
5237     /* Should we put the compiled-in defaults here instead? */\r
5238     i = 0;\r
5239     customColors[i++] = lightSquareColor & 0xffffff;\r
5240     customColors[i++] = darkSquareColor & 0xffffff;\r
5241     customColors[i++] = whitePieceColor & 0xffffff;\r
5242     customColors[i++] = blackPieceColor & 0xffffff;\r
5243     customColors[i++] = highlightSquareColor & 0xffffff;\r
5244     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5245 \r
5246     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5247       customColors[i++] = textAttribs[ccl].color;\r
5248     }\r
5249     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5250     firstTime = FALSE;\r
5251   }\r
5252 \r
5253   cc.lStructSize = sizeof(cc);\r
5254   cc.hwndOwner = hwnd;\r
5255   cc.hInstance = NULL;\r
5256   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5257   cc.lpCustColors = (LPDWORD) customColors;\r
5258   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5259 \r
5260   if (!ChooseColor(&cc)) return FALSE;\r
5261 \r
5262   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5263   if (newcolor == *which) return FALSE;\r
5264   *which = newcolor;\r
5265   return TRUE;\r
5266 \r
5267   /*\r
5268   InitDrawingColors();\r
5269   InvalidateRect(hwnd, &boardRect, FALSE);\r
5270   */\r
5271 }\r
5272 \r
5273 BOOLEAN\r
5274 MyLoadSound(MySound *ms)\r
5275 {\r
5276   BOOL ok = FALSE;\r
5277   struct stat st;\r
5278   FILE *f;\r
5279 \r
5280   if (ms->data) free(ms->data);\r
5281   ms->data = NULL;\r
5282 \r
5283   switch (ms->name[0]) {\r
5284   case NULLCHAR:\r
5285     /* Silence */\r
5286     ok = TRUE;\r
5287     break;\r
5288   case '$':\r
5289     /* System sound from Control Panel.  Don't preload here. */\r
5290     ok = TRUE;\r
5291     break;\r
5292   case '!':\r
5293     if (ms->name[1] == NULLCHAR) {\r
5294       /* "!" alone = silence */\r
5295       ok = TRUE;\r
5296     } else {\r
5297       /* Builtin wave resource.  Error if not found. */\r
5298       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5299       if (h == NULL) break;\r
5300       ms->data = (void *)LoadResource(hInst, h);\r
5301       if (h == NULL) break;\r
5302       ok = TRUE;\r
5303     }\r
5304     break;\r
5305   default:\r
5306     /* .wav file.  Error if not found. */\r
5307     f = fopen(ms->name, "rb");\r
5308     if (f == NULL) break;\r
5309     if (fstat(fileno(f), &st) < 0) break;\r
5310     ms->data = malloc(st.st_size);\r
5311     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5312     fclose(f);\r
5313     ok = TRUE;\r
5314     break;\r
5315   }\r
5316   if (!ok) {\r
5317     char buf[MSG_SIZ];\r
5318     sprintf(buf, "Error loading sound %s", ms->name);\r
5319     DisplayError(buf, GetLastError());\r
5320   }\r
5321   return ok;\r
5322 }\r
5323 \r
5324 BOOLEAN\r
5325 MyPlaySound(MySound *ms)\r
5326 {\r
5327   BOOLEAN ok = FALSE;\r
5328 \r
5329   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5330   switch (ms->name[0]) {\r
5331   case NULLCHAR:\r
5332         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5333     /* Silence */\r
5334     ok = TRUE;\r
5335     break;\r
5336   case '$':\r
5337     /* System sound from Control Panel (deprecated feature).\r
5338        "$" alone or an unset sound name gets default beep (still in use). */\r
5339     if (ms->name[1]) {\r
5340       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5341     }\r
5342     if (!ok) ok = MessageBeep(MB_OK);\r
5343     break; \r
5344   case '!':\r
5345     /* Builtin wave resource, or "!" alone for silence */\r
5346     if (ms->name[1]) {\r
5347       if (ms->data == NULL) return FALSE;\r
5348       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5349     } else {\r
5350       ok = TRUE;\r
5351     }\r
5352     break;\r
5353   default:\r
5354     /* .wav file.  Error if not found. */\r
5355     if (ms->data == NULL) return FALSE;\r
5356     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5357     break;\r
5358   }\r
5359   /* Don't print an error: this can happen innocently if the sound driver\r
5360      is busy; for instance, if another instance of WinBoard is playing\r
5361      a sound at about the same time. */\r
5362   return ok;\r
5363 }\r
5364 \r
5365 \r
5366 LRESULT CALLBACK\r
5367 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5368 {\r
5369   BOOL ok;\r
5370   OPENFILENAME *ofn;\r
5371   static UINT *number; /* gross that this is static */\r
5372 \r
5373   switch (message) {\r
5374   case WM_INITDIALOG: /* message: initialize dialog box */\r
5375     /* Center the dialog over the application window */\r
5376     ofn = (OPENFILENAME *) lParam;\r
5377     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5378       number = (UINT *) ofn->lCustData;\r
5379       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5380     } else {\r
5381       number = NULL;\r
5382     }\r
5383     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5384     return FALSE;  /* Allow for further processing */\r
5385 \r
5386   case WM_COMMAND:\r
5387     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5388       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5389     }\r
5390     return FALSE;  /* Allow for further processing */\r
5391   }\r
5392   return FALSE;\r
5393 }\r
5394 \r
5395 UINT APIENTRY\r
5396 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5397 {\r
5398   static UINT *number;\r
5399   OPENFILENAME *ofname;\r
5400   OFNOTIFY *ofnot;\r
5401   switch (uiMsg) {\r
5402   case WM_INITDIALOG:\r
5403     ofname = (OPENFILENAME *)lParam;\r
5404     number = (UINT *)(ofname->lCustData);\r
5405     break;\r
5406   case WM_NOTIFY:\r
5407     ofnot = (OFNOTIFY *)lParam;\r
5408     if (ofnot->hdr.code == CDN_FILEOK) {\r
5409       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5410     }\r
5411     break;\r
5412   }\r
5413   return 0;\r
5414 }\r
5415 \r
5416 \r
5417 FILE *\r
5418 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5419                char *nameFilt, char *dlgTitle, UINT *number,\r
5420                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5421 {\r
5422   OPENFILENAME openFileName;\r
5423   char buf1[MSG_SIZ];\r
5424   FILE *f;\r
5425 \r
5426   if (fileName == NULL) fileName = buf1;\r
5427   if (defName == NULL) {\r
5428     strcpy(fileName, "*.");\r
5429     strcat(fileName, defExt);\r
5430   } else {\r
5431     strcpy(fileName, defName);\r
5432   }\r
5433   if (fileTitle) strcpy(fileTitle, "");\r
5434   if (number) *number = 0;\r
5435 \r
5436   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5437   openFileName.hwndOwner         = hwnd;\r
5438   openFileName.hInstance         = (HANDLE) hInst;\r
5439   openFileName.lpstrFilter       = nameFilt;\r
5440   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5441   openFileName.nMaxCustFilter    = 0L;\r
5442   openFileName.nFilterIndex      = 1L;\r
5443   openFileName.lpstrFile         = fileName;\r
5444   openFileName.nMaxFile          = MSG_SIZ;\r
5445   openFileName.lpstrFileTitle    = fileTitle;\r
5446   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5447   openFileName.lpstrInitialDir   = NULL;\r
5448   openFileName.lpstrTitle        = dlgTitle;\r
5449   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5450     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5451     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5452     | (oldDialog ? 0 : OFN_EXPLORER);\r
5453   openFileName.nFileOffset       = 0;\r
5454   openFileName.nFileExtension    = 0;\r
5455   openFileName.lpstrDefExt       = defExt;\r
5456   openFileName.lCustData         = (LONG) number;\r
5457   openFileName.lpfnHook          = oldDialog ?\r
5458     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5459   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5460 \r
5461   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5462                         GetOpenFileName(&openFileName)) {\r
5463     /* open the file */\r
5464     f = fopen(openFileName.lpstrFile, write);\r
5465     if (f == NULL) {\r
5466       MessageBox(hwnd, "File open failed", NULL,\r
5467                  MB_OK|MB_ICONEXCLAMATION);\r
5468       return NULL;\r
5469     }\r
5470   } else {\r
5471     int err = CommDlgExtendedError();\r
5472     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5473     return FALSE;\r
5474   }\r
5475   return f;\r
5476 }\r
5477 \r
5478 \r
5479 \r
5480 VOID APIENTRY\r
5481 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5482 {\r
5483   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5484 \r
5485   /*\r
5486    * Get the first pop-up menu in the menu template. This is the\r
5487    * menu that TrackPopupMenu displays.\r
5488    */\r
5489   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5490 \r
5491   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5492 \r
5493   /*\r
5494    * TrackPopup uses screen coordinates, so convert the\r
5495    * coordinates of the mouse click to screen coordinates.\r
5496    */\r
5497   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5498 \r
5499   /* Draw and track the floating pop-up menu. */\r
5500   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5501                  pt.x, pt.y, 0, hwnd, NULL);\r
5502 \r
5503   /* Destroy the menu.*/\r
5504   DestroyMenu(hmenu);\r
5505 }\r
5506    \r
5507 typedef struct {\r
5508   HWND hDlg, hText;\r
5509   int sizeX, sizeY, newSizeX, newSizeY;\r
5510   HDWP hdwp;\r
5511 } ResizeEditPlusButtonsClosure;\r
5512 \r
5513 BOOL CALLBACK\r
5514 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5515 {\r
5516   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5517   RECT rect;\r
5518   POINT pt;\r
5519 \r
5520   if (hChild == cl->hText) return TRUE;\r
5521   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5522   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5523   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5524   ScreenToClient(cl->hDlg, &pt);\r
5525   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5526     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5527   return TRUE;\r
5528 }\r
5529 \r
5530 /* Resize a dialog that has a (rich) edit field filling most of\r
5531    the top, with a row of buttons below */\r
5532 VOID\r
5533 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5534 {\r
5535   RECT rectText;\r
5536   int newTextHeight, newTextWidth;\r
5537   ResizeEditPlusButtonsClosure cl;\r
5538   \r
5539   /*if (IsIconic(hDlg)) return;*/\r
5540   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5541   \r
5542   cl.hdwp = BeginDeferWindowPos(8);\r
5543 \r
5544   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5545   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5546   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5547   if (newTextHeight < 0) {\r
5548     newSizeY += -newTextHeight;\r
5549     newTextHeight = 0;\r
5550   }\r
5551   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5552     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5553 \r
5554   cl.hDlg = hDlg;\r
5555   cl.hText = hText;\r
5556   cl.sizeX = sizeX;\r
5557   cl.sizeY = sizeY;\r
5558   cl.newSizeX = newSizeX;\r
5559   cl.newSizeY = newSizeY;\r
5560   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5561 \r
5562   EndDeferWindowPos(cl.hdwp);\r
5563 }\r
5564 \r
5565 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5566 {\r
5567     RECT    rChild, rParent;\r
5568     int     wChild, hChild, wParent, hParent;\r
5569     int     wScreen, hScreen, xNew, yNew;\r
5570     HDC     hdc;\r
5571 \r
5572     /* Get the Height and Width of the child window */\r
5573     GetWindowRect (hwndChild, &rChild);\r
5574     wChild = rChild.right - rChild.left;\r
5575     hChild = rChild.bottom - rChild.top;\r
5576 \r
5577     /* Get the Height and Width of the parent window */\r
5578     GetWindowRect (hwndParent, &rParent);\r
5579     wParent = rParent.right - rParent.left;\r
5580     hParent = rParent.bottom - rParent.top;\r
5581 \r
5582     /* Get the display limits */\r
5583     hdc = GetDC (hwndChild);\r
5584     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5585     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5586     ReleaseDC(hwndChild, hdc);\r
5587 \r
5588     /* Calculate new X position, then adjust for screen */\r
5589     xNew = rParent.left + ((wParent - wChild) /2);\r
5590     if (xNew < 0) {\r
5591         xNew = 0;\r
5592     } else if ((xNew+wChild) > wScreen) {\r
5593         xNew = wScreen - wChild;\r
5594     }\r
5595 \r
5596     /* Calculate new Y position, then adjust for screen */\r
5597     if( mode == 0 ) {\r
5598         yNew = rParent.top  + ((hParent - hChild) /2);\r
5599     }\r
5600     else {\r
5601         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5602     }\r
5603 \r
5604     if (yNew < 0) {\r
5605         yNew = 0;\r
5606     } else if ((yNew+hChild) > hScreen) {\r
5607         yNew = hScreen - hChild;\r
5608     }\r
5609 \r
5610     /* Set it, and return */\r
5611     return SetWindowPos (hwndChild, NULL,\r
5612                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5613 }\r
5614 \r
5615 /* Center one window over another */\r
5616 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5617 {\r
5618     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5619 }\r
5620 \r
5621 /*---------------------------------------------------------------------------*\\r
5622  *\r
5623  * Startup Dialog functions\r
5624  *\r
5625 \*---------------------------------------------------------------------------*/\r
5626 void\r
5627 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5628 {\r
5629   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5630 \r
5631   while (*cd != NULL) {\r
5632     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5633     cd++;\r
5634   }\r
5635 }\r
5636 \r
5637 void\r
5638 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5639 {\r
5640   char buf1[MAX_ARG_LEN];\r
5641   int len;\r
5642 \r
5643   if (str[0] == '@') {\r
5644     FILE* f = fopen(str + 1, "r");\r
5645     if (f == NULL) {\r
5646       DisplayFatalError(str + 1, errno, 2);\r
5647       return;\r
5648     }\r
5649     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5650     fclose(f);\r
5651     buf1[len] = NULLCHAR;\r
5652     str = buf1;\r
5653   }\r
5654 \r
5655   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5656 \r
5657   for (;;) {\r
5658     char buf[MSG_SIZ];\r
5659     char *end = strchr(str, '\n');\r
5660     if (end == NULL) return;\r
5661     memcpy(buf, str, end - str);\r
5662     buf[end - str] = NULLCHAR;\r
5663     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5664     str = end + 1;\r
5665   }\r
5666 }\r
5667 \r
5668 void\r
5669 SetStartupDialogEnables(HWND hDlg)\r
5670 {\r
5671   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5672     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5673     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5674   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5675     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5676   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5677     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5678   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5679     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5680   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5681     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5682     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5683     IsDlgButtonChecked(hDlg, OPT_View));\r
5684 }\r
5685 \r
5686 char *\r
5687 QuoteForFilename(char *filename)\r
5688 {\r
5689   int dquote, space;\r
5690   dquote = strchr(filename, '"') != NULL;\r
5691   space = strchr(filename, ' ') != NULL;\r
5692   if (dquote || space) {\r
5693     if (dquote) {\r
5694       return "'";\r
5695     } else {\r
5696       return "\"";\r
5697     }\r
5698   } else {\r
5699     return "";\r
5700   }\r
5701 }\r
5702 \r
5703 VOID\r
5704 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5705 {\r
5706   char buf[MSG_SIZ];\r
5707   char *q;\r
5708 \r
5709   InitComboStringsFromOption(hwndCombo, nthnames);\r
5710   q = QuoteForFilename(nthcp);\r
5711   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5712   if (*nthdir != NULLCHAR) {\r
5713     q = QuoteForFilename(nthdir);\r
5714     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5715   }\r
5716   if (*nthcp == NULLCHAR) {\r
5717     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5718   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5719     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5720     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5721   }\r
5722 }\r
5723 \r
5724 LRESULT CALLBACK\r
5725 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5726 {\r
5727   char buf[MSG_SIZ];\r
5728   HANDLE hwndCombo;\r
5729   char *p;\r
5730 \r
5731   switch (message) {\r
5732   case WM_INITDIALOG:\r
5733     /* Center the dialog */\r
5734     CenterWindow (hDlg, GetDesktopWindow());\r
5735     /* Initialize the dialog items */\r
5736     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5737                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5738                   firstChessProgramNames);\r
5739     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5740                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5741                   secondChessProgramNames);\r
5742     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5743     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5744     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5745     if (*appData.icsHelper != NULLCHAR) {\r
5746       char *q = QuoteForFilename(appData.icsHelper);\r
5747       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5748     }\r
5749     if (*appData.icsHost == NULLCHAR) {\r
5750       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5751       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5752     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5753       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5754       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5755     }\r
5756 \r
5757     if (appData.icsActive) {\r
5758       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5759     }\r
5760     else if (appData.noChessProgram) {\r
5761       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5762     }\r
5763     else {\r
5764       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5765     }\r
5766 \r
5767     SetStartupDialogEnables(hDlg);\r
5768     return TRUE;\r
5769 \r
5770   case WM_COMMAND:\r
5771     switch (LOWORD(wParam)) {\r
5772     case IDOK:\r
5773       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5774         strcpy(buf, "/fcp=");\r
5775         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5776         p = buf;\r
5777         ParseArgs(StringGet, &p);\r
5778         strcpy(buf, "/scp=");\r
5779         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5780         p = buf;\r
5781         ParseArgs(StringGet, &p);\r
5782         appData.noChessProgram = FALSE;\r
5783         appData.icsActive = FALSE;\r
5784       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5785         strcpy(buf, "/ics /icshost=");\r
5786         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5787         p = buf;\r
5788         ParseArgs(StringGet, &p);\r
5789         if (appData.zippyPlay) {\r
5790           strcpy(buf, "/fcp=");\r
5791           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5792           p = buf;\r
5793           ParseArgs(StringGet, &p);\r
5794         }\r
5795       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5796         appData.noChessProgram = TRUE;\r
5797         appData.icsActive = FALSE;\r
5798       } else {\r
5799         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5800                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5801         return TRUE;\r
5802       }\r
5803       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5804         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5805         p = buf;\r
5806         ParseArgs(StringGet, &p);\r
5807       }\r
5808       EndDialog(hDlg, TRUE);\r
5809       return TRUE;\r
5810 \r
5811     case IDCANCEL:\r
5812       ExitEvent(0);\r
5813       return TRUE;\r
5814 \r
5815     case IDM_HELPCONTENTS:\r
5816       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5817         MessageBox (GetFocus(),\r
5818                     "Unable to activate help",\r
5819                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5820       }\r
5821       break;\r
5822 \r
5823     default:\r
5824       SetStartupDialogEnables(hDlg);\r
5825       break;\r
5826     }\r
5827     break;\r
5828   }\r
5829   return FALSE;\r
5830 }\r
5831 \r
5832 /*---------------------------------------------------------------------------*\\r
5833  *\r
5834  * About box dialog functions\r
5835  *\r
5836 \*---------------------------------------------------------------------------*/\r
5837 \r
5838 /* Process messages for "About" dialog box */\r
5839 LRESULT CALLBACK\r
5840 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5841 {\r
5842   switch (message) {\r
5843   case WM_INITDIALOG: /* message: initialize dialog box */\r
5844     /* Center the dialog over the application window */\r
5845     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5846     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5847     JAWS_COPYRIGHT\r
5848     return (TRUE);\r
5849 \r
5850   case WM_COMMAND: /* message: received a command */\r
5851     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5852         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5853       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5854       return (TRUE);\r
5855     }\r
5856     break;\r
5857   }\r
5858   return (FALSE);\r
5859 }\r
5860 \r
5861 /*---------------------------------------------------------------------------*\\r
5862  *\r
5863  * Comment Dialog functions\r
5864  *\r
5865 \*---------------------------------------------------------------------------*/\r
5866 \r
5867 LRESULT CALLBACK\r
5868 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5869 {\r
5870   static HANDLE hwndText = NULL;\r
5871   int len, newSizeX, newSizeY, flags;\r
5872   static int sizeX, sizeY;\r
5873   char *str;\r
5874   RECT rect;\r
5875   MINMAXINFO *mmi;\r
5876 \r
5877   switch (message) {\r
5878   case WM_INITDIALOG: /* message: initialize dialog box */\r
5879     /* Initialize the dialog items */\r
5880     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5881     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5882     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5883     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5884     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5885     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5886     SetWindowText(hDlg, commentTitle);\r
5887     if (editComment) {\r
5888       SetFocus(hwndText);\r
5889     } else {\r
5890       SetFocus(GetDlgItem(hDlg, IDOK));\r
5891     }\r
5892     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5893                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5894                 MAKELPARAM(FALSE, 0));\r
5895     /* Size and position the dialog */\r
5896     if (!commentDialog) {\r
5897       commentDialog = hDlg;\r
5898       flags = SWP_NOZORDER;\r
5899       GetClientRect(hDlg, &rect);\r
5900       sizeX = rect.right;\r
5901       sizeY = rect.bottom;\r
5902       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5903           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5904         WINDOWPLACEMENT wp;\r
5905         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5906         wp.length = sizeof(WINDOWPLACEMENT);\r
5907         wp.flags = 0;\r
5908         wp.showCmd = SW_SHOW;\r
5909         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5910         wp.rcNormalPosition.left = wpComment.x;\r
5911         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5912         wp.rcNormalPosition.top = wpComment.y;\r
5913         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5914         SetWindowPlacement(hDlg, &wp);\r
5915 \r
5916         GetClientRect(hDlg, &rect);\r
5917         newSizeX = rect.right;\r
5918         newSizeY = rect.bottom;\r
5919         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5920                               newSizeX, newSizeY);\r
5921         sizeX = newSizeX;\r
5922         sizeY = newSizeY;\r
5923       }\r
5924     }\r
5925     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
5926     return FALSE;\r
5927 \r
5928   case WM_COMMAND: /* message: received a command */\r
5929     switch (LOWORD(wParam)) {\r
5930     case IDOK:\r
5931       if (editComment) {\r
5932         char *p, *q;\r
5933         /* Read changed options from the dialog box */\r
5934         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5935         len = GetWindowTextLength(hwndText);\r
5936         str = (char *) malloc(len + 1);\r
5937         GetWindowText(hwndText, str, len + 1);\r
5938         p = q = str;\r
5939         while (*q) {\r
5940           if (*q == '\r')\r
5941             q++;\r
5942           else\r
5943             *p++ = *q++;\r
5944         }\r
5945         *p = NULLCHAR;\r
5946         ReplaceComment(commentIndex, str);\r
5947         free(str);\r
5948       }\r
5949       CommentPopDown();\r
5950       return TRUE;\r
5951 \r
5952     case IDCANCEL:\r
5953     case OPT_CancelComment:\r
5954       CommentPopDown();\r
5955       return TRUE;\r
5956 \r
5957     case OPT_ClearComment:\r
5958       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5959       break;\r
5960 \r
5961     case OPT_EditComment:\r
5962       EditCommentEvent();\r
5963       return TRUE;\r
5964 \r
5965     default:\r
5966       break;\r
5967     }\r
5968     break;\r
5969 \r
5970   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
5971         if( wParam == OPT_CommentText ) {\r
5972             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
5973 \r
5974             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
5975                 POINTL pt;\r
5976                 LRESULT index;\r
5977 \r
5978                 pt.x = LOWORD( lpMF->lParam );\r
5979                 pt.y = HIWORD( lpMF->lParam );\r
5980 \r
5981                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
5982 \r
5983                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
5984                 len = GetWindowTextLength(hwndText);\r
5985                 str = (char *) malloc(len + 1);\r
5986                 GetWindowText(hwndText, str, len + 1);\r
5987                 ReplaceComment(commentIndex, str);\r
5988                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
5989                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
5990                 free(str);\r
5991 \r
5992                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
5993                 lpMF->msg = WM_USER;\r
5994 \r
5995                 return TRUE;\r
5996             }\r
5997         }\r
5998         break;\r
5999 \r
6000   case WM_SIZE:\r
6001     newSizeX = LOWORD(lParam);\r
6002     newSizeY = HIWORD(lParam);\r
6003     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6004     sizeX = newSizeX;\r
6005     sizeY = newSizeY;\r
6006     break;\r
6007 \r
6008   case WM_GETMINMAXINFO:\r
6009     /* Prevent resizing window too small */\r
6010     mmi = (MINMAXINFO *) lParam;\r
6011     mmi->ptMinTrackSize.x = 100;\r
6012     mmi->ptMinTrackSize.y = 100;\r
6013     break;\r
6014   }\r
6015   return FALSE;\r
6016 }\r
6017 \r
6018 VOID\r
6019 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6020 {\r
6021   FARPROC lpProc;\r
6022   char *p, *q;\r
6023 \r
6024   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6025 \r
6026   if (str == NULL) str = "";\r
6027   p = (char *) malloc(2 * strlen(str) + 2);\r
6028   q = p;\r
6029   while (*str) {\r
6030     if (*str == '\n') *q++ = '\r';\r
6031     *q++ = *str++;\r
6032   }\r
6033   *q = NULLCHAR;\r
6034   if (commentText != NULL) free(commentText);\r
6035 \r
6036   commentIndex = index;\r
6037   commentTitle = title;\r
6038   commentText = p;\r
6039   editComment = edit;\r
6040 \r
6041   if (commentDialog) {\r
6042     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6043     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6044   } else {\r
6045     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6046     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6047                  hwndMain, (DLGPROC)lpProc);\r
6048     FreeProcInstance(lpProc);\r
6049   }\r
6050   commentUp = TRUE;\r
6051 }\r
6052 \r
6053 \r
6054 /*---------------------------------------------------------------------------*\\r
6055  *\r
6056  * Type-in move dialog functions\r
6057  * \r
6058 \*---------------------------------------------------------------------------*/\r
6059 \r
6060 LRESULT CALLBACK\r
6061 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6062 {\r
6063   char move[MSG_SIZ];\r
6064   HWND hInput;\r
6065   ChessMove moveType;\r
6066   int fromX, fromY, toX, toY;\r
6067   char promoChar;\r
6068 \r
6069   switch (message) {\r
6070   case WM_INITDIALOG:\r
6071     move[0] = (char) lParam;\r
6072     move[1] = NULLCHAR;\r
6073     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6074     hInput = GetDlgItem(hDlg, OPT_Move);\r
6075     SetWindowText(hInput, move);\r
6076     SetFocus(hInput);\r
6077     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6078     return FALSE;\r
6079 \r
6080   case WM_COMMAND:\r
6081     switch (LOWORD(wParam)) {\r
6082     case IDOK:\r
6083       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6084       { int n; Board board;\r
6085         // [HGM] FENedit\r
6086         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6087                 EditPositionPasteFEN(move);\r
6088                 EndDialog(hDlg, TRUE);\r
6089                 return TRUE;\r
6090         }\r
6091         // [HGM] movenum: allow move number to be typed in any mode\r
6092         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6093           ToNrEvent(2*n-1);\r
6094           EndDialog(hDlg, TRUE);\r
6095           return TRUE;\r
6096         }\r
6097       }\r
6098       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6099         gameMode != Training) {\r
6100         DisplayMoveError("Displayed move is not current");\r
6101       } else {\r
6102 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6103         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6104           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6105         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6106         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6107           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6108           if (gameMode != Training)\r
6109               forwardMostMove = currentMove;\r
6110           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6111         } else {\r
6112           DisplayMoveError("Could not parse move");\r
6113         }\r
6114       }\r
6115       EndDialog(hDlg, TRUE);\r
6116       return TRUE;\r
6117     case IDCANCEL:\r
6118       EndDialog(hDlg, FALSE);\r
6119       return TRUE;\r
6120     default:\r
6121       break;\r
6122     }\r
6123     break;\r
6124   }\r
6125   return FALSE;\r
6126 }\r
6127 \r
6128 VOID\r
6129 PopUpMoveDialog(char firstchar)\r
6130 {\r
6131     FARPROC lpProc;\r
6132     \r
6133     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6134         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6135         gameMode == AnalyzeMode || gameMode == EditGame || \r
6136         gameMode == EditPosition || gameMode == IcsExamining ||\r
6137         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6138         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6139                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6140                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6141         gameMode == Training) {\r
6142       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6143       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6144         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6145       FreeProcInstance(lpProc);\r
6146     }\r
6147 }\r
6148 \r
6149 /*---------------------------------------------------------------------------*\\r
6150  *\r
6151  * Type-in name dialog functions\r
6152  * \r
6153 \*---------------------------------------------------------------------------*/\r
6154 \r
6155 LRESULT CALLBACK\r
6156 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6157 {\r
6158   char move[MSG_SIZ];\r
6159   HWND hInput;\r
6160 \r
6161   switch (message) {\r
6162   case WM_INITDIALOG:\r
6163     move[0] = (char) lParam;\r
6164     move[1] = NULLCHAR;\r
6165     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6166     hInput = GetDlgItem(hDlg, OPT_Name);\r
6167     SetWindowText(hInput, move);\r
6168     SetFocus(hInput);\r
6169     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6170     return FALSE;\r
6171 \r
6172   case WM_COMMAND:\r
6173     switch (LOWORD(wParam)) {\r
6174     case IDOK:\r
6175       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6176       appData.userName = strdup(move);\r
6177       SetUserLogo();\r
6178       SetGameInfo();\r
6179       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6180         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6181         DisplayTitle(move);\r
6182       }\r
6183 \r
6184 \r
6185       EndDialog(hDlg, TRUE);\r
6186       return TRUE;\r
6187     case IDCANCEL:\r
6188       EndDialog(hDlg, FALSE);\r
6189       return TRUE;\r
6190     default:\r
6191       break;\r
6192     }\r
6193     break;\r
6194   }\r
6195   return FALSE;\r
6196 }\r
6197 \r
6198 VOID\r
6199 PopUpNameDialog(char firstchar)\r
6200 {\r
6201     FARPROC lpProc;\r
6202     \r
6203       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6204       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6205         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6206       FreeProcInstance(lpProc);\r
6207 }\r
6208 \r
6209 /*---------------------------------------------------------------------------*\\r
6210  *\r
6211  *  Error dialogs\r
6212  * \r
6213 \*---------------------------------------------------------------------------*/\r
6214 \r
6215 /* Nonmodal error box */\r
6216 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6217                              WPARAM wParam, LPARAM lParam);\r
6218 \r
6219 VOID\r
6220 ErrorPopUp(char *title, char *content)\r
6221 {\r
6222   FARPROC lpProc;\r
6223   char *p, *q;\r
6224   BOOLEAN modal = hwndMain == NULL;\r
6225 \r
6226   p = content;\r
6227   q = errorMessage;\r
6228   while (*p) {\r
6229     if (*p == '\n') {\r
6230       if (modal) {\r
6231         *q++ = ' ';\r
6232         p++;\r
6233       } else {\r
6234         *q++ = '\r';\r
6235         *q++ = *p++;\r
6236       }\r
6237     } else {\r
6238       *q++ = *p++;\r
6239     }\r
6240   }\r
6241   *q = NULLCHAR;\r
6242   strncpy(errorTitle, title, sizeof(errorTitle));\r
6243   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6244   \r
6245   if (modal) {\r
6246     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6247   } else {\r
6248     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6249     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6250                  hwndMain, (DLGPROC)lpProc);\r
6251     FreeProcInstance(lpProc);\r
6252   }\r
6253 }\r
6254 \r
6255 VOID\r
6256 ErrorPopDown()\r
6257 {\r
6258   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6259   if (errorDialog == NULL) return;\r
6260   DestroyWindow(errorDialog);\r
6261   errorDialog = NULL;\r
6262   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6263 }\r
6264 \r
6265 LRESULT CALLBACK\r
6266 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6267 {\r
6268   HANDLE hwndText;\r
6269   RECT rChild;\r
6270 \r
6271   switch (message) {\r
6272   case WM_INITDIALOG:\r
6273     GetWindowRect(hDlg, &rChild);\r
6274 \r
6275     /*\r
6276     SetWindowPos(hDlg, NULL, rChild.left,\r
6277       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6278       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6279     */\r
6280 \r
6281     /* \r
6282         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6283         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6284         and it doesn't work when you resize the dialog.\r
6285         For now, just give it a default position.\r
6286     */\r
6287     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6288 \r
6289     errorDialog = hDlg;\r
6290     SetWindowText(hDlg, errorTitle);\r
6291     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6292     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6293     return FALSE;\r
6294 \r
6295   case WM_COMMAND:\r
6296     switch (LOWORD(wParam)) {\r
6297     case IDOK:\r
6298     case IDCANCEL:\r
6299       if (errorDialog == hDlg) errorDialog = NULL;\r
6300       DestroyWindow(hDlg);\r
6301       return TRUE;\r
6302 \r
6303     default:\r
6304       break;\r
6305     }\r
6306     break;\r
6307   }\r
6308   return FALSE;\r
6309 }\r
6310 \r
6311 #ifdef GOTHIC\r
6312 HWND gothicDialog = NULL;\r
6313 \r
6314 LRESULT CALLBACK\r
6315 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6316 {\r
6317   HANDLE hwndText;\r
6318   RECT rChild;\r
6319   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6320 \r
6321   switch (message) {\r
6322   case WM_INITDIALOG:\r
6323     GetWindowRect(hDlg, &rChild);\r
6324 \r
6325     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6326                                                              SWP_NOZORDER);\r
6327 \r
6328     /* \r
6329         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6330         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6331         and it doesn't work when you resize the dialog.\r
6332         For now, just give it a default position.\r
6333     */\r
6334     gothicDialog = hDlg;\r
6335     SetWindowText(hDlg, errorTitle);\r
6336     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6337     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6338     return FALSE;\r
6339 \r
6340   case WM_COMMAND:\r
6341     switch (LOWORD(wParam)) {\r
6342     case IDOK:\r
6343     case IDCANCEL:\r
6344       if (errorDialog == hDlg) errorDialog = NULL;\r
6345       DestroyWindow(hDlg);\r
6346       return TRUE;\r
6347 \r
6348     default:\r
6349       break;\r
6350     }\r
6351     break;\r
6352   }\r
6353   return FALSE;\r
6354 }\r
6355 \r
6356 VOID\r
6357 GothicPopUp(char *title, VariantClass variant)\r
6358 {\r
6359   FARPROC lpProc;\r
6360   static char *lastTitle;\r
6361 \r
6362   strncpy(errorTitle, title, sizeof(errorTitle));\r
6363   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6364 \r
6365   if(lastTitle != title && gothicDialog != NULL) {\r
6366     DestroyWindow(gothicDialog);\r
6367     gothicDialog = NULL;\r
6368   }\r
6369   if(variant != VariantNormal && gothicDialog == NULL) {\r
6370     title = lastTitle;\r
6371     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6372     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6373                  hwndMain, (DLGPROC)lpProc);\r
6374     FreeProcInstance(lpProc);\r
6375   }\r
6376 }\r
6377 #endif\r
6378 \r
6379 /*---------------------------------------------------------------------------*\\r
6380  *\r
6381  *  Ics Interaction console functions\r
6382  *\r
6383 \*---------------------------------------------------------------------------*/\r
6384 \r
6385 #define HISTORY_SIZE 64\r
6386 static char *history[HISTORY_SIZE];\r
6387 int histIn = 0, histP = 0;\r
6388 \r
6389 VOID\r
6390 SaveInHistory(char *cmd)\r
6391 {\r
6392   if (history[histIn] != NULL) {\r
6393     free(history[histIn]);\r
6394     history[histIn] = NULL;\r
6395   }\r
6396   if (*cmd == NULLCHAR) return;\r
6397   history[histIn] = StrSave(cmd);\r
6398   histIn = (histIn + 1) % HISTORY_SIZE;\r
6399   if (history[histIn] != NULL) {\r
6400     free(history[histIn]);\r
6401     history[histIn] = NULL;\r
6402   }\r
6403   histP = histIn;\r
6404 }\r
6405 \r
6406 char *\r
6407 PrevInHistory(char *cmd)\r
6408 {\r
6409   int newhp;\r
6410   if (histP == histIn) {\r
6411     if (history[histIn] != NULL) free(history[histIn]);\r
6412     history[histIn] = StrSave(cmd);\r
6413   }\r
6414   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6415   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6416   histP = newhp;\r
6417   return history[histP];\r
6418 }\r
6419 \r
6420 char *\r
6421 NextInHistory()\r
6422 {\r
6423   if (histP == histIn) return NULL;\r
6424   histP = (histP + 1) % HISTORY_SIZE;\r
6425   return history[histP];   \r
6426 }\r
6427 \r
6428 HMENU\r
6429 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6430 {\r
6431   HMENU hmenu, h;\r
6432   int i = 0;\r
6433   hmenu = LoadMenu(hInst, "TextMenu");\r
6434   h = GetSubMenu(hmenu, 0);\r
6435   while (e->item) {\r
6436     if (strcmp(e->item, "-") == 0) {\r
6437       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6438     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6439       int flags = MF_STRING, j = 0;\r
6440       if (e->item[0] == '|') {\r
6441         flags |= MF_MENUBARBREAK;\r
6442         j++;\r
6443       }\r
6444       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6445       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6446     }\r
6447     e++;\r
6448     i++;\r
6449   } \r
6450   return hmenu;\r
6451 }\r
6452 \r
6453 WNDPROC consoleTextWindowProc;\r
6454 \r
6455 void\r
6456 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6457 {\r
6458   char buf[MSG_SIZ], name[MSG_SIZ];\r
6459   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6460   CHARRANGE sel;\r
6461 \r
6462   if (!getname) {\r
6463     SetWindowText(hInput, command);\r
6464     if (immediate) {\r
6465       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6466     } else {\r
6467       sel.cpMin = 999999;\r
6468       sel.cpMax = 999999;\r
6469       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6470       SetFocus(hInput);\r
6471     }\r
6472     return;\r
6473   }    \r
6474   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6475   if (sel.cpMin == sel.cpMax) {\r
6476     /* Expand to surrounding word */\r
6477     TEXTRANGE tr;\r
6478     do {\r
6479       tr.chrg.cpMax = sel.cpMin;\r
6480       tr.chrg.cpMin = --sel.cpMin;\r
6481       if (sel.cpMin < 0) break;\r
6482       tr.lpstrText = name;\r
6483       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6484     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6485     sel.cpMin++;\r
6486 \r
6487     do {\r
6488       tr.chrg.cpMin = sel.cpMax;\r
6489       tr.chrg.cpMax = ++sel.cpMax;\r
6490       tr.lpstrText = name;\r
6491       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6492     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6493     sel.cpMax--;\r
6494 \r
6495     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6496       MessageBeep(MB_ICONEXCLAMATION);\r
6497       return;\r
6498     }\r
6499     tr.chrg = sel;\r
6500     tr.lpstrText = name;\r
6501     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6502   } else {\r
6503     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6504       MessageBeep(MB_ICONEXCLAMATION);\r
6505       return;\r
6506     }\r
6507     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6508   }\r
6509   if (immediate) {\r
6510     sprintf(buf, "%s %s", command, name);\r
6511     SetWindowText(hInput, buf);\r
6512     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6513   } else {\r
6514     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6515     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6516     SetWindowText(hInput, buf);\r
6517     sel.cpMin = 999999;\r
6518     sel.cpMax = 999999;\r
6519     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6520     SetFocus(hInput);\r
6521   }\r
6522 }\r
6523 \r
6524 LRESULT CALLBACK \r
6525 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6526 {\r
6527   HWND hInput;\r
6528   CHARRANGE sel;\r
6529 \r
6530   switch (message) {\r
6531   case WM_KEYDOWN:\r
6532     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6533     switch (wParam) {\r
6534     case VK_PRIOR:\r
6535       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6536       return 0;\r
6537     case VK_NEXT:\r
6538       sel.cpMin = 999999;\r
6539       sel.cpMax = 999999;\r
6540       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6541       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6542       return 0;\r
6543     }\r
6544     break;\r
6545   case WM_CHAR:\r
6546    if(wParam != '\022') {\r
6547     if (wParam == '\t') {\r
6548       if (GetKeyState(VK_SHIFT) < 0) {\r
6549         /* shifted */\r
6550         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6551         if (buttonDesc[0].hwnd) {\r
6552           SetFocus(buttonDesc[0].hwnd);\r
6553         } else {\r
6554           SetFocus(hwndMain);\r
6555         }\r
6556       } else {\r
6557         /* unshifted */\r
6558         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6559       }\r
6560     } else {\r
6561       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6562       JAWS_DELETE( SetFocus(hInput); )\r
6563       SendMessage(hInput, message, wParam, lParam);\r
6564     }\r
6565     return 0;\r
6566    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6567   case WM_RBUTTONDOWN:\r
6568     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6569       /* Move selection here if it was empty */\r
6570       POINT pt;\r
6571       pt.x = LOWORD(lParam);\r
6572       pt.y = HIWORD(lParam);\r
6573       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6574       if (sel.cpMin == sel.cpMax) {\r
6575         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6576         sel.cpMax = sel.cpMin;\r
6577         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6578       }\r
6579       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6580 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6581       POINT pt;\r
6582       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6583       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6584       if (sel.cpMin == sel.cpMax) {\r
6585         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6586         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6587       }\r
6588       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6589         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6590       }\r
6591       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6592       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6593       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6594       MenuPopup(hwnd, pt, hmenu, -1);\r
6595 }\r
6596     }\r
6597     return 0;\r
6598   case WM_RBUTTONUP:\r
6599     if (GetKeyState(VK_SHIFT) & ~1) {\r
6600       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6601         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6602     }\r
6603     return 0;\r
6604   case WM_PASTE:\r
6605     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6606     SetFocus(hInput);\r
6607     return SendMessage(hInput, message, wParam, lParam);\r
6608   case WM_MBUTTONDOWN:\r
6609     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6610   case WM_COMMAND:\r
6611     switch (LOWORD(wParam)) {\r
6612     case IDM_QuickPaste:\r
6613       {\r
6614         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6615         if (sel.cpMin == sel.cpMax) {\r
6616           MessageBeep(MB_ICONEXCLAMATION);\r
6617           return 0;\r
6618         }\r
6619         SendMessage(hwnd, WM_COPY, 0, 0);\r
6620         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6621         SendMessage(hInput, WM_PASTE, 0, 0);\r
6622         SetFocus(hInput);\r
6623         return 0;\r
6624       }\r
6625     case IDM_Cut:\r
6626       SendMessage(hwnd, WM_CUT, 0, 0);\r
6627       return 0;\r
6628     case IDM_Paste:\r
6629       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6630       return 0;\r
6631     case IDM_Copy:\r
6632       SendMessage(hwnd, WM_COPY, 0, 0);\r
6633       return 0;\r
6634     default:\r
6635       {\r
6636         int i = LOWORD(wParam) - IDM_CommandX;\r
6637         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6638             icsTextMenuEntry[i].command != NULL) {\r
6639           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6640                    icsTextMenuEntry[i].getname,\r
6641                    icsTextMenuEntry[i].immediate);\r
6642           return 0;\r
6643         }\r
6644       }\r
6645       break;\r
6646     }\r
6647     break;\r
6648   }\r
6649   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6650 }\r
6651 \r
6652 WNDPROC consoleInputWindowProc;\r
6653 \r
6654 LRESULT CALLBACK\r
6655 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6656 {\r
6657   char buf[MSG_SIZ];\r
6658   char *p;\r
6659   static BOOL sendNextChar = FALSE;\r
6660   static BOOL quoteNextChar = FALSE;\r
6661   InputSource *is = consoleInputSource;\r
6662   CHARFORMAT cf;\r
6663   CHARRANGE sel;\r
6664 \r
6665   switch (message) {\r
6666   case WM_CHAR:\r
6667     if (!appData.localLineEditing || sendNextChar) {\r
6668       is->buf[0] = (CHAR) wParam;\r
6669       is->count = 1;\r
6670       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6671       sendNextChar = FALSE;\r
6672       return 0;\r
6673     }\r
6674     if (quoteNextChar) {\r
6675       buf[0] = (char) wParam;\r
6676       buf[1] = NULLCHAR;\r
6677       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6678       quoteNextChar = FALSE;\r
6679       return 0;\r
6680     }\r
6681     switch (wParam) {\r
6682     case '\r':   /* Enter key */\r
6683       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6684       if (consoleEcho) SaveInHistory(is->buf);\r
6685       is->buf[is->count++] = '\n';\r
6686       is->buf[is->count] = NULLCHAR;\r
6687       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6688       if (consoleEcho) {\r
6689         ConsoleOutput(is->buf, is->count, TRUE);\r
6690       } else if (appData.localLineEditing) {\r
6691         ConsoleOutput("\n", 1, TRUE);\r
6692       }\r
6693       /* fall thru */\r
6694     case '\033': /* Escape key */\r
6695       SetWindowText(hwnd, "");\r
6696       cf.cbSize = sizeof(CHARFORMAT);\r
6697       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6698       if (consoleEcho) {\r
6699         cf.crTextColor = textAttribs[ColorNormal].color;\r
6700       } else {\r
6701         cf.crTextColor = COLOR_ECHOOFF;\r
6702       }\r
6703       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6704       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6705       return 0;\r
6706     case '\t':   /* Tab key */\r
6707       if (GetKeyState(VK_SHIFT) < 0) {\r
6708         /* shifted */\r
6709         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6710       } else {\r
6711         /* unshifted */\r
6712         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6713         if (buttonDesc[0].hwnd) {\r
6714           SetFocus(buttonDesc[0].hwnd);\r
6715         } else {\r
6716           SetFocus(hwndMain);\r
6717         }\r
6718       }\r
6719       return 0;\r
6720     case '\023': /* Ctrl+S */\r
6721       sendNextChar = TRUE;\r
6722       return 0;\r
6723     case '\021': /* Ctrl+Q */\r
6724       quoteNextChar = TRUE;\r
6725       return 0;\r
6726     JAWS_REPLAY\r
6727     default:\r
6728       break;\r
6729     }\r
6730     break;\r
6731   case WM_KEYDOWN:\r
6732     switch (wParam) {\r
6733     case VK_UP:\r
6734       GetWindowText(hwnd, buf, MSG_SIZ);\r
6735       p = PrevInHistory(buf);\r
6736       if (p != NULL) {\r
6737         SetWindowText(hwnd, p);\r
6738         sel.cpMin = 999999;\r
6739         sel.cpMax = 999999;\r
6740         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6741         return 0;\r
6742       }\r
6743       break;\r
6744     case VK_DOWN:\r
6745       p = NextInHistory();\r
6746       if (p != NULL) {\r
6747         SetWindowText(hwnd, p);\r
6748         sel.cpMin = 999999;\r
6749         sel.cpMax = 999999;\r
6750         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6751         return 0;\r
6752       }\r
6753       break;\r
6754     case VK_HOME:\r
6755     case VK_END:\r
6756       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6757       /* fall thru */\r
6758     case VK_PRIOR:\r
6759     case VK_NEXT:\r
6760       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6761       return 0;\r
6762     }\r
6763     break;\r
6764   case WM_MBUTTONDOWN:\r
6765     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6766       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6767     break;\r
6768   case WM_RBUTTONUP:\r
6769     if (GetKeyState(VK_SHIFT) & ~1) {\r
6770       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6771         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6772     } else {\r
6773       POINT pt;\r
6774       HMENU hmenu;\r
6775       hmenu = LoadMenu(hInst, "InputMenu");\r
6776       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6777       if (sel.cpMin == sel.cpMax) {\r
6778         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6779         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6780       }\r
6781       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6782         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6783       }\r
6784       pt.x = LOWORD(lParam);\r
6785       pt.y = HIWORD(lParam);\r
6786       MenuPopup(hwnd, pt, hmenu, -1);\r
6787     }\r
6788     return 0;\r
6789   case WM_COMMAND:\r
6790     switch (LOWORD(wParam)) { \r
6791     case IDM_Undo:\r
6792       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6793       return 0;\r
6794     case IDM_SelectAll:\r
6795       sel.cpMin = 0;\r
6796       sel.cpMax = -1; /*999999?*/\r
6797       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6798       return 0;\r
6799     case IDM_Cut:\r
6800       SendMessage(hwnd, WM_CUT, 0, 0);\r
6801       return 0;\r
6802     case IDM_Paste:\r
6803       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6804       return 0;\r
6805     case IDM_Copy:\r
6806       SendMessage(hwnd, WM_COPY, 0, 0);\r
6807       return 0;\r
6808     }\r
6809     break;\r
6810   }\r
6811   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6812 }\r
6813 \r
6814 #define CO_MAX  100000\r
6815 #define CO_TRIM   1000\r
6816 \r
6817 LRESULT CALLBACK\r
6818 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6819 {\r
6820   static SnapData sd;\r
6821   HWND hText, hInput;\r
6822   RECT rect;\r
6823   static int sizeX, sizeY;\r
6824   int newSizeX, newSizeY;\r
6825   MINMAXINFO *mmi;\r
6826   WORD wMask;\r
6827 \r
6828   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6829   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6830 \r
6831   switch (message) {\r
6832   case WM_NOTIFY:\r
6833     if (((NMHDR*)lParam)->code == EN_LINK)\r
6834     {\r
6835       ENLINK *pLink = (ENLINK*)lParam;\r
6836       if (pLink->msg == WM_LBUTTONUP)\r
6837       {\r
6838         TEXTRANGE tr;\r
6839 \r
6840         tr.chrg = pLink->chrg;\r
6841         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
6842         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
6843         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
6844         free(tr.lpstrText);\r
6845       }\r
6846     }\r
6847     break;\r
6848   case WM_INITDIALOG: /* message: initialize dialog box */\r
6849     hwndConsole = hDlg;\r
6850     SetFocus(hInput);\r
6851     consoleTextWindowProc = (WNDPROC)\r
6852       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6853     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6854     consoleInputWindowProc = (WNDPROC)\r
6855       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6856     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6857     Colorize(ColorNormal, TRUE);\r
6858     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6859     ChangedConsoleFont();\r
6860     GetClientRect(hDlg, &rect);\r
6861     sizeX = rect.right;\r
6862     sizeY = rect.bottom;\r
6863     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
6864         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
6865       WINDOWPLACEMENT wp;\r
6866       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6867       wp.length = sizeof(WINDOWPLACEMENT);\r
6868       wp.flags = 0;\r
6869       wp.showCmd = SW_SHOW;\r
6870       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6871       wp.rcNormalPosition.left = wpConsole.x;\r
6872       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6873       wp.rcNormalPosition.top = wpConsole.y;\r
6874       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6875       SetWindowPlacement(hDlg, &wp);\r
6876     }\r
6877 \r
6878    // [HGM] Chessknight's change 2004-07-13\r
6879    else { /* Determine Defaults */\r
6880        WINDOWPLACEMENT wp;\r
6881        wpConsole.x = wpMain.width + 1;\r
6882        wpConsole.y = wpMain.y;\r
6883        wpConsole.width = screenWidth -  wpMain.width;\r
6884        wpConsole.height = wpMain.height;\r
6885        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6886        wp.length = sizeof(WINDOWPLACEMENT);\r
6887        wp.flags = 0;\r
6888        wp.showCmd = SW_SHOW;\r
6889        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6890        wp.rcNormalPosition.left = wpConsole.x;\r
6891        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6892        wp.rcNormalPosition.top = wpConsole.y;\r
6893        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6894        SetWindowPlacement(hDlg, &wp);\r
6895     }\r
6896 \r
6897    // Allow hText to highlight URLs and send notifications on them\r
6898    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
6899    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
6900    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
6901    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
6902 \r
6903     return FALSE;\r
6904 \r
6905   case WM_SETFOCUS:\r
6906     SetFocus(hInput);\r
6907     return 0;\r
6908 \r
6909   case WM_CLOSE:\r
6910     ExitEvent(0);\r
6911     /* not reached */\r
6912     break;\r
6913 \r
6914   case WM_SIZE:\r
6915     if (IsIconic(hDlg)) break;\r
6916     newSizeX = LOWORD(lParam);\r
6917     newSizeY = HIWORD(lParam);\r
6918     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6919       RECT rectText, rectInput;\r
6920       POINT pt;\r
6921       int newTextHeight, newTextWidth;\r
6922       GetWindowRect(hText, &rectText);\r
6923       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6924       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6925       if (newTextHeight < 0) {\r
6926         newSizeY += -newTextHeight;\r
6927         newTextHeight = 0;\r
6928       }\r
6929       SetWindowPos(hText, NULL, 0, 0,\r
6930         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6931       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6932       pt.x = rectInput.left;\r
6933       pt.y = rectInput.top + newSizeY - sizeY;\r
6934       ScreenToClient(hDlg, &pt);\r
6935       SetWindowPos(hInput, NULL, \r
6936         pt.x, pt.y, /* needs client coords */   \r
6937         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6938         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6939     }\r
6940     sizeX = newSizeX;\r
6941     sizeY = newSizeY;\r
6942     break;\r
6943 \r
6944   case WM_GETMINMAXINFO:\r
6945     /* Prevent resizing window too small */\r
6946     mmi = (MINMAXINFO *) lParam;\r
6947     mmi->ptMinTrackSize.x = 100;\r
6948     mmi->ptMinTrackSize.y = 100;\r
6949     break;\r
6950 \r
6951   /* [AS] Snapping */\r
6952   case WM_ENTERSIZEMOVE:\r
6953     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
6954 \r
6955   case WM_SIZING:\r
6956     return OnSizing( &sd, hDlg, wParam, lParam );\r
6957 \r
6958   case WM_MOVING:\r
6959     return OnMoving( &sd, hDlg, wParam, lParam );\r
6960 \r
6961   case WM_EXITSIZEMOVE:\r
6962         UpdateICSWidth(hText);\r
6963     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
6964   }\r
6965 \r
6966   return DefWindowProc(hDlg, message, wParam, lParam);\r
6967 }\r
6968 \r
6969 \r
6970 VOID\r
6971 ConsoleCreate()\r
6972 {\r
6973   HWND hCons;\r
6974   if (hwndConsole) return;\r
6975   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6976   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6977 }\r
6978 \r
6979 \r
6980 VOID\r
6981 ConsoleOutput(char* data, int length, int forceVisible)\r
6982 {\r
6983   HWND hText;\r
6984   int trim, exlen;\r
6985   char *p, *q;\r
6986   char buf[CO_MAX+1];\r
6987   POINT pEnd;\r
6988   RECT rect;\r
6989   static int delayLF = 0;\r
6990   CHARRANGE savesel, sel;\r
6991 \r
6992   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6993   p = data;\r
6994   q = buf;\r
6995   if (delayLF) {\r
6996     *q++ = '\r';\r
6997     *q++ = '\n';\r
6998     delayLF = 0;\r
6999   }\r
7000   while (length--) {\r
7001     if (*p == '\n') {\r
7002       if (*++p) {\r
7003         *q++ = '\r';\r
7004         *q++ = '\n';\r
7005       } else {\r
7006         delayLF = 1;\r
7007       }\r
7008     } else if (*p == '\007') {\r
7009        MyPlaySound(&sounds[(int)SoundBell]);\r
7010        p++;\r
7011     } else {\r
7012       *q++ = *p++;\r
7013     }\r
7014   }\r
7015   *q = NULLCHAR;\r
7016   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7017   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7018   /* Save current selection */\r
7019   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7020   exlen = GetWindowTextLength(hText);\r
7021   /* Find out whether current end of text is visible */\r
7022   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7023   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7024   /* Trim existing text if it's too long */\r
7025   if (exlen + (q - buf) > CO_MAX) {\r
7026     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7027     sel.cpMin = 0;\r
7028     sel.cpMax = trim;\r
7029     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7030     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7031     exlen -= trim;\r
7032     savesel.cpMin -= trim;\r
7033     savesel.cpMax -= trim;\r
7034     if (exlen < 0) exlen = 0;\r
7035     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7036     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7037   }\r
7038   /* Append the new text */\r
7039   sel.cpMin = exlen;\r
7040   sel.cpMax = exlen;\r
7041   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7042   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7043   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7044   if (forceVisible || exlen == 0 ||\r
7045       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7046        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7047     /* Scroll to make new end of text visible if old end of text\r
7048        was visible or new text is an echo of user typein */\r
7049     sel.cpMin = 9999999;\r
7050     sel.cpMax = 9999999;\r
7051     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7052     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7053     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7054     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7055   }\r
7056   if (savesel.cpMax == exlen || forceVisible) {\r
7057     /* Move insert point to new end of text if it was at the old\r
7058        end of text or if the new text is an echo of user typein */\r
7059     sel.cpMin = 9999999;\r
7060     sel.cpMax = 9999999;\r
7061     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7062   } else {\r
7063     /* Restore previous selection */\r
7064     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7065   }\r
7066   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7067 }\r
7068 \r
7069 /*---------*/\r
7070 \r
7071 \r
7072 void\r
7073 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7074 {\r
7075   char buf[100];\r
7076   char *str;\r
7077   COLORREF oldFg, oldBg;\r
7078   HFONT oldFont;\r
7079   RECT rect;\r
7080 \r
7081   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7082 \r
7083   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7084   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7085   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7086 \r
7087   rect.left = x;\r
7088   rect.right = x + squareSize;\r
7089   rect.top  = y;\r
7090   rect.bottom = y + squareSize;\r
7091   str = buf;\r
7092 \r
7093   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7094                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7095              y, ETO_CLIPPED|ETO_OPAQUE,\r
7096              &rect, str, strlen(str), NULL);\r
7097 \r
7098   (void) SetTextColor(hdc, oldFg);\r
7099   (void) SetBkColor(hdc, oldBg);\r
7100   (void) SelectObject(hdc, oldFont);\r
7101 }\r
7102 \r
7103 void\r
7104 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7105               RECT *rect, char *color, char *flagFell)\r
7106 {\r
7107   char buf[100];\r
7108   char *str;\r
7109   COLORREF oldFg, oldBg;\r
7110   HFONT oldFont;\r
7111 \r
7112   if (appData.clockMode) {\r
7113     if (tinyLayout)\r
7114       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7115     else\r
7116       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7117     str = buf;\r
7118   } else {\r
7119     str = color;\r
7120   }\r
7121 \r
7122   if (highlight) {\r
7123     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7124     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7125   } else {\r
7126     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7127     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7128   }\r
7129   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7130 \r
7131   JAWS_SILENCE\r
7132 \r
7133   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7134              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7135              rect, str, strlen(str), NULL);\r
7136   if(logoHeight > 0 && appData.clockMode) {\r
7137       RECT r;\r
7138       sprintf(buf, "%s %s", buf+7, flagFell);\r
7139       r.top = rect->top + logoHeight/2;\r
7140       r.left = rect->left;\r
7141       r.right = rect->right;\r
7142       r.bottom = rect->bottom;\r
7143       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7144                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7145                  &r, str, strlen(str), NULL);\r
7146   }\r
7147   (void) SetTextColor(hdc, oldFg);\r
7148   (void) SetBkColor(hdc, oldBg);\r
7149   (void) SelectObject(hdc, oldFont);\r
7150 }\r
7151 \r
7152 \r
7153 int\r
7154 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7155            OVERLAPPED *ovl)\r
7156 {\r
7157   int ok, err;\r
7158 \r
7159   /* [AS]  */\r
7160   if( count <= 0 ) {\r
7161     if (appData.debugMode) {\r
7162       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7163     }\r
7164 \r
7165     return ERROR_INVALID_USER_BUFFER;\r
7166   }\r
7167 \r
7168   ResetEvent(ovl->hEvent);\r
7169   ovl->Offset = ovl->OffsetHigh = 0;\r
7170   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7171   if (ok) {\r
7172     err = NO_ERROR;\r
7173   } else {\r
7174     err = GetLastError();\r
7175     if (err == ERROR_IO_PENDING) {\r
7176       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7177       if (ok)\r
7178         err = NO_ERROR;\r
7179       else\r
7180         err = GetLastError();\r
7181     }\r
7182   }\r
7183   return err;\r
7184 }\r
7185 \r
7186 int\r
7187 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7188             OVERLAPPED *ovl)\r
7189 {\r
7190   int ok, err;\r
7191 \r
7192   ResetEvent(ovl->hEvent);\r
7193   ovl->Offset = ovl->OffsetHigh = 0;\r
7194   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7195   if (ok) {\r
7196     err = NO_ERROR;\r
7197   } else {\r
7198     err = GetLastError();\r
7199     if (err == ERROR_IO_PENDING) {\r
7200       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7201       if (ok)\r
7202         err = NO_ERROR;\r
7203       else\r
7204         err = GetLastError();\r
7205     }\r
7206   }\r
7207   return err;\r
7208 }\r
7209 \r
7210 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7211 void CheckForInputBufferFull( InputSource * is )\r
7212 {\r
7213     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7214         /* Look for end of line */\r
7215         char * p = is->buf;\r
7216         \r
7217         while( p < is->next && *p != '\n' ) {\r
7218             p++;\r
7219         }\r
7220 \r
7221         if( p >= is->next ) {\r
7222             if (appData.debugMode) {\r
7223                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7224             }\r
7225 \r
7226             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7227             is->count = (DWORD) -1;\r
7228             is->next = is->buf;\r
7229         }\r
7230     }\r
7231 }\r
7232 \r
7233 DWORD\r
7234 InputThread(LPVOID arg)\r
7235 {\r
7236   InputSource *is;\r
7237   OVERLAPPED ovl;\r
7238 \r
7239   is = (InputSource *) arg;\r
7240   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7241   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7242   while (is->hThread != NULL) {\r
7243     is->error = DoReadFile(is->hFile, is->next,\r
7244                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7245                            &is->count, &ovl);\r
7246     if (is->error == NO_ERROR) {\r
7247       is->next += is->count;\r
7248     } else {\r
7249       if (is->error == ERROR_BROKEN_PIPE) {\r
7250         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7251         is->count = 0;\r
7252       } else {\r
7253         is->count = (DWORD) -1;\r
7254         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7255         break; \r
7256       }\r
7257     }\r
7258 \r
7259     CheckForInputBufferFull( is );\r
7260 \r
7261     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7262 \r
7263     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7264 \r
7265     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7266   }\r
7267 \r
7268   CloseHandle(ovl.hEvent);\r
7269   CloseHandle(is->hFile);\r
7270 \r
7271   if (appData.debugMode) {\r
7272     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7273   }\r
7274 \r
7275   return 0;\r
7276 }\r
7277 \r
7278 \r
7279 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7280 DWORD\r
7281 NonOvlInputThread(LPVOID arg)\r
7282 {\r
7283   InputSource *is;\r
7284   char *p, *q;\r
7285   int i;\r
7286   char prev;\r
7287 \r
7288   is = (InputSource *) arg;\r
7289   while (is->hThread != NULL) {\r
7290     is->error = ReadFile(is->hFile, is->next,\r
7291                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7292                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7293     if (is->error == NO_ERROR) {\r
7294       /* Change CRLF to LF */\r
7295       if (is->next > is->buf) {\r
7296         p = is->next - 1;\r
7297         i = is->count + 1;\r
7298       } else {\r
7299         p = is->next;\r
7300         i = is->count;\r
7301       }\r
7302       q = p;\r
7303       prev = NULLCHAR;\r
7304       while (i > 0) {\r
7305         if (prev == '\r' && *p == '\n') {\r
7306           *(q-1) = '\n';\r
7307           is->count--;\r
7308         } else { \r
7309           *q++ = *p;\r
7310         }\r
7311         prev = *p++;\r
7312         i--;\r
7313       }\r
7314       *q = NULLCHAR;\r
7315       is->next = q;\r
7316     } else {\r
7317       if (is->error == ERROR_BROKEN_PIPE) {\r
7318         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7319         is->count = 0; \r
7320       } else {\r
7321         is->count = (DWORD) -1;\r
7322       }\r
7323     }\r
7324 \r
7325     CheckForInputBufferFull( is );\r
7326 \r
7327     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7328 \r
7329     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7330 \r
7331     if (is->count < 0) break;  /* Quit on error */\r
7332   }\r
7333   CloseHandle(is->hFile);\r
7334   return 0;\r
7335 }\r
7336 \r
7337 DWORD\r
7338 SocketInputThread(LPVOID arg)\r
7339 {\r
7340   InputSource *is;\r
7341 \r
7342   is = (InputSource *) arg;\r
7343   while (is->hThread != NULL) {\r
7344     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7345     if ((int)is->count == SOCKET_ERROR) {\r
7346       is->count = (DWORD) -1;\r
7347       is->error = WSAGetLastError();\r
7348     } else {\r
7349       is->error = NO_ERROR;\r
7350       is->next += is->count;\r
7351       if (is->count == 0 && is->second == is) {\r
7352         /* End of file on stderr; quit with no message */\r
7353         break;\r
7354       }\r
7355     }\r
7356     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7357 \r
7358     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7359 \r
7360     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7361   }\r
7362   return 0;\r
7363 }\r
7364 \r
7365 VOID\r
7366 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7367 {\r
7368   InputSource *is;\r
7369 \r
7370   is = (InputSource *) lParam;\r
7371   if (is->lineByLine) {\r
7372     /* Feed in lines one by one */\r
7373     char *p = is->buf;\r
7374     char *q = p;\r
7375     while (q < is->next) {\r
7376       if (*q++ == '\n') {\r
7377         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7378         p = q;\r
7379       }\r
7380     }\r
7381     \r
7382     /* Move any partial line to the start of the buffer */\r
7383     q = is->buf;\r
7384     while (p < is->next) {\r
7385       *q++ = *p++;\r
7386     }\r
7387     is->next = q;\r
7388 \r
7389     if (is->error != NO_ERROR || is->count == 0) {\r
7390       /* Notify backend of the error.  Note: If there was a partial\r
7391          line at the end, it is not flushed through. */\r
7392       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7393     }\r
7394   } else {\r
7395     /* Feed in the whole chunk of input at once */\r
7396     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7397     is->next = is->buf;\r
7398   }\r
7399 }\r
7400 \r
7401 /*---------------------------------------------------------------------------*\\r
7402  *\r
7403  *  Menu enables. Used when setting various modes.\r
7404  *\r
7405 \*---------------------------------------------------------------------------*/\r
7406 \r
7407 typedef struct {\r
7408   int item;\r
7409   int flags;\r
7410 } Enables;\r
7411 \r
7412 VOID\r
7413 GreyRevert(Boolean grey)\r
7414 { // [HGM] vari: for retracting variations in local mode\r
7415   HMENU hmenu = GetMenu(hwndMain);\r
7416   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7417   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7418 }\r
7419 \r
7420 VOID\r
7421 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7422 {\r
7423   while (enab->item > 0) {\r
7424     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7425     enab++;\r
7426   }\r
7427 }\r
7428 \r
7429 Enables gnuEnables[] = {\r
7430   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7431   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7432   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7433   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7434   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7435   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7436   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7437   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7438   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7439   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7440   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7441   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7442   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7443   { -1, -1 }\r
7444 };\r
7445 \r
7446 Enables icsEnables[] = {\r
7447   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7448   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7449   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7450   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7451   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7452   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7453   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7454   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7455   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7456   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7457   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7458   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7459   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7460   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7461   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7462   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7463   { -1, -1 }\r
7464 };\r
7465 \r
7466 #if ZIPPY\r
7467 Enables zippyEnables[] = {\r
7468   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7469   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7470   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7471   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7472   { -1, -1 }\r
7473 };\r
7474 #endif\r
7475 \r
7476 Enables ncpEnables[] = {\r
7477   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7478   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7479   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7480   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7481   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7482   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7483   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7484   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7485   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7486   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7487   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7488   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7489   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7490   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7491   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7492   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7493   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7494   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7495   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7496   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7497   { -1, -1 }\r
7498 };\r
7499 \r
7500 Enables trainingOnEnables[] = {\r
7501   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7502   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7503   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7504   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7505   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7506   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7507   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7508   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7509   { -1, -1 }\r
7510 };\r
7511 \r
7512 Enables trainingOffEnables[] = {\r
7513   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7514   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7515   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7516   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7517   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7518   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7519   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7520   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7521   { -1, -1 }\r
7522 };\r
7523 \r
7524 /* These modify either ncpEnables or gnuEnables */\r
7525 Enables cmailEnables[] = {\r
7526   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7527   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7528   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7529   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7530   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7531   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7532   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7533   { -1, -1 }\r
7534 };\r
7535 \r
7536 Enables machineThinkingEnables[] = {\r
7537   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7538   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7539   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7540   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7541   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7542   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7543   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7544   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7545   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7546   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7547   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7548   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7549   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7550   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7551   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7552   { -1, -1 }\r
7553 };\r
7554 \r
7555 Enables userThinkingEnables[] = {\r
7556   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7557   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7558   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7559   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7560   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7561   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7562   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7563   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7564   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7565   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7566   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7567   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7568   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7569   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7570   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7571   { -1, -1 }\r
7572 };\r
7573 \r
7574 /*---------------------------------------------------------------------------*\\r
7575  *\r
7576  *  Front-end interface functions exported by XBoard.\r
7577  *  Functions appear in same order as prototypes in frontend.h.\r
7578  * \r
7579 \*---------------------------------------------------------------------------*/\r
7580 VOID\r
7581 ModeHighlight()\r
7582 {\r
7583   static UINT prevChecked = 0;\r
7584   static int prevPausing = 0;\r
7585   UINT nowChecked;\r
7586 \r
7587   if (pausing != prevPausing) {\r
7588     prevPausing = pausing;\r
7589     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7590                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7591     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7592   }\r
7593 \r
7594   switch (gameMode) {\r
7595   case BeginningOfGame:\r
7596     if (appData.icsActive)\r
7597       nowChecked = IDM_IcsClient;\r
7598     else if (appData.noChessProgram)\r
7599       nowChecked = IDM_EditGame;\r
7600     else\r
7601       nowChecked = IDM_MachineBlack;\r
7602     break;\r
7603   case MachinePlaysBlack:\r
7604     nowChecked = IDM_MachineBlack;\r
7605     break;\r
7606   case MachinePlaysWhite:\r
7607     nowChecked = IDM_MachineWhite;\r
7608     break;\r
7609   case TwoMachinesPlay:\r
7610     nowChecked = IDM_TwoMachines;\r
7611     break;\r
7612   case AnalyzeMode:\r
7613     nowChecked = IDM_AnalysisMode;\r
7614     break;\r
7615   case AnalyzeFile:\r
7616     nowChecked = IDM_AnalyzeFile;\r
7617     break;\r
7618   case EditGame:\r
7619     nowChecked = IDM_EditGame;\r
7620     break;\r
7621   case PlayFromGameFile:\r
7622     nowChecked = IDM_LoadGame;\r
7623     break;\r
7624   case EditPosition:\r
7625     nowChecked = IDM_EditPosition;\r
7626     break;\r
7627   case Training:\r
7628     nowChecked = IDM_Training;\r
7629     break;\r
7630   case IcsPlayingWhite:\r
7631   case IcsPlayingBlack:\r
7632   case IcsObserving:\r
7633   case IcsIdle:\r
7634     nowChecked = IDM_IcsClient;\r
7635     break;\r
7636   default:\r
7637   case EndOfGame:\r
7638     nowChecked = 0;\r
7639     break;\r
7640   }\r
7641   if (prevChecked != 0)\r
7642     (void) CheckMenuItem(GetMenu(hwndMain),\r
7643                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7644   if (nowChecked != 0)\r
7645     (void) CheckMenuItem(GetMenu(hwndMain),\r
7646                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7647 \r
7648   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7649     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7650                           MF_BYCOMMAND|MF_ENABLED);\r
7651   } else {\r
7652     (void) EnableMenuItem(GetMenu(hwndMain), \r
7653                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7654   }\r
7655 \r
7656   prevChecked = nowChecked;\r
7657 \r
7658   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7659   if (appData.icsActive) {\r
7660        if (appData.icsEngineAnalyze) {\r
7661                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7662                        MF_BYCOMMAND|MF_CHECKED);\r
7663        } else {\r
7664                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7665                        MF_BYCOMMAND|MF_UNCHECKED);\r
7666        }\r
7667   }\r
7668 }\r
7669 \r
7670 VOID\r
7671 SetICSMode()\r
7672 {\r
7673   HMENU hmenu = GetMenu(hwndMain);\r
7674   SetMenuEnables(hmenu, icsEnables);\r
7675   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7676     MF_BYPOSITION|MF_ENABLED);\r
7677 #if ZIPPY\r
7678   if (appData.zippyPlay) {\r
7679     SetMenuEnables(hmenu, zippyEnables);\r
7680     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7681          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7682           MF_BYCOMMAND|MF_ENABLED);\r
7683   }\r
7684 #endif\r
7685 }\r
7686 \r
7687 VOID\r
7688 SetGNUMode()\r
7689 {\r
7690   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7691 }\r
7692 \r
7693 VOID\r
7694 SetNCPMode()\r
7695 {\r
7696   HMENU hmenu = GetMenu(hwndMain);\r
7697   SetMenuEnables(hmenu, ncpEnables);\r
7698   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7699     MF_BYPOSITION|MF_GRAYED);\r
7700     DrawMenuBar(hwndMain);\r
7701 }\r
7702 \r
7703 VOID\r
7704 SetCmailMode()\r
7705 {\r
7706   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7707 }\r
7708 \r
7709 VOID \r
7710 SetTrainingModeOn()\r
7711 {\r
7712   int i;\r
7713   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7714   for (i = 0; i < N_BUTTONS; i++) {\r
7715     if (buttonDesc[i].hwnd != NULL)\r
7716       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7717   }\r
7718   CommentPopDown();\r
7719 }\r
7720 \r
7721 VOID SetTrainingModeOff()\r
7722 {\r
7723   int i;\r
7724   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7725   for (i = 0; i < N_BUTTONS; i++) {\r
7726     if (buttonDesc[i].hwnd != NULL)\r
7727       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7728   }\r
7729 }\r
7730 \r
7731 \r
7732 VOID\r
7733 SetUserThinkingEnables()\r
7734 {\r
7735   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7736 }\r
7737 \r
7738 VOID\r
7739 SetMachineThinkingEnables()\r
7740 {\r
7741   HMENU hMenu = GetMenu(hwndMain);\r
7742   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7743 \r
7744   SetMenuEnables(hMenu, machineThinkingEnables);\r
7745 \r
7746   if (gameMode == MachinePlaysBlack) {\r
7747     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7748   } else if (gameMode == MachinePlaysWhite) {\r
7749     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7750   } else if (gameMode == TwoMachinesPlay) {\r
7751     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7752   }\r
7753 }\r
7754 \r
7755 \r
7756 VOID\r
7757 DisplayTitle(char *str)\r
7758 {\r
7759   char title[MSG_SIZ], *host;\r
7760   if (str[0] != NULLCHAR) {\r
7761     strcpy(title, str);\r
7762   } else if (appData.icsActive) {\r
7763     if (appData.icsCommPort[0] != NULLCHAR)\r
7764       host = "ICS";\r
7765     else \r
7766       host = appData.icsHost;\r
7767     sprintf(title, "%s: %s", szTitle, host);\r
7768   } else if (appData.noChessProgram) {\r
7769     strcpy(title, szTitle);\r
7770   } else {\r
7771     strcpy(title, szTitle);\r
7772     strcat(title, ": ");\r
7773     strcat(title, first.tidy);\r
7774   }\r
7775   SetWindowText(hwndMain, title);\r
7776 }\r
7777 \r
7778 \r
7779 VOID\r
7780 DisplayMessage(char *str1, char *str2)\r
7781 {\r
7782   HDC hdc;\r
7783   HFONT oldFont;\r
7784   int remain = MESSAGE_TEXT_MAX - 1;\r
7785   int len;\r
7786 \r
7787   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7788   messageText[0] = NULLCHAR;\r
7789   if (*str1) {\r
7790     len = strlen(str1);\r
7791     if (len > remain) len = remain;\r
7792     strncpy(messageText, str1, len);\r
7793     messageText[len] = NULLCHAR;\r
7794     remain -= len;\r
7795   }\r
7796   if (*str2 && remain >= 2) {\r
7797     if (*str1) {\r
7798       strcat(messageText, "  ");\r
7799       remain -= 2;\r
7800     }\r
7801     len = strlen(str2);\r
7802     if (len > remain) len = remain;\r
7803     strncat(messageText, str2, len);\r
7804   }\r
7805   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7806 \r
7807   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
7808 \r
7809   SAYMACHINEMOVE();\r
7810 \r
7811   hdc = GetDC(hwndMain);\r
7812   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7813   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7814              &messageRect, messageText, strlen(messageText), NULL);\r
7815   (void) SelectObject(hdc, oldFont);\r
7816   (void) ReleaseDC(hwndMain, hdc);\r
7817 }\r
7818 \r
7819 VOID\r
7820 DisplayError(char *str, int error)\r
7821 {\r
7822   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7823   int len;\r
7824 \r
7825   if (error == 0) {\r
7826     strcpy(buf, str);\r
7827   } else {\r
7828     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7829                         NULL, error, LANG_NEUTRAL,\r
7830                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7831     if (len > 0) {\r
7832       sprintf(buf, "%s:\n%s", str, buf2);\r
7833     } else {\r
7834       ErrorMap *em = errmap;\r
7835       while (em->err != 0 && em->err != error) em++;\r
7836       if (em->err != 0) {\r
7837         sprintf(buf, "%s:\n%s", str, em->msg);\r
7838       } else {\r
7839         sprintf(buf, "%s:\nError code %d", str, error);\r
7840       }\r
7841     }\r
7842   }\r
7843   \r
7844   ErrorPopUp("Error", buf);\r
7845 }\r
7846 \r
7847 \r
7848 VOID\r
7849 DisplayMoveError(char *str)\r
7850 {\r
7851   fromX = fromY = -1;\r
7852   ClearHighlights();\r
7853   DrawPosition(FALSE, NULL);\r
7854   if (appData.popupMoveErrors) {\r
7855     ErrorPopUp("Error", str);\r
7856   } else {\r
7857     DisplayMessage(str, "");\r
7858     moveErrorMessageUp = TRUE;\r
7859   }\r
7860 }\r
7861 \r
7862 VOID\r
7863 DisplayFatalError(char *str, int error, int exitStatus)\r
7864 {\r
7865   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7866   int len;\r
7867   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7868 \r
7869   if (error != 0) {\r
7870     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7871                         NULL, error, LANG_NEUTRAL,\r
7872                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7873     if (len > 0) {\r
7874       sprintf(buf, "%s:\n%s", str, buf2);\r
7875     } else {\r
7876       ErrorMap *em = errmap;\r
7877       while (em->err != 0 && em->err != error) em++;\r
7878       if (em->err != 0) {\r
7879         sprintf(buf, "%s:\n%s", str, em->msg);\r
7880       } else {\r
7881         sprintf(buf, "%s:\nError code %d", str, error);\r
7882       }\r
7883     }\r
7884     str = buf;\r
7885   }\r
7886   if (appData.debugMode) {\r
7887     fprintf(debugFP, "%s: %s\n", label, str);\r
7888   }\r
7889   if (appData.popupExitMessage) {\r
7890     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7891                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7892   }\r
7893   ExitEvent(exitStatus);\r
7894 }\r
7895 \r
7896 \r
7897 VOID\r
7898 DisplayInformation(char *str)\r
7899 {\r
7900   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7901 }\r
7902 \r
7903 \r
7904 VOID\r
7905 DisplayNote(char *str)\r
7906 {\r
7907   ErrorPopUp("Note", str);\r
7908 }\r
7909 \r
7910 \r
7911 typedef struct {\r
7912   char *title, *question, *replyPrefix;\r
7913   ProcRef pr;\r
7914 } QuestionParams;\r
7915 \r
7916 LRESULT CALLBACK\r
7917 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7918 {\r
7919   static QuestionParams *qp;\r
7920   char reply[MSG_SIZ];\r
7921   int len, err;\r
7922 \r
7923   switch (message) {\r
7924   case WM_INITDIALOG:\r
7925     qp = (QuestionParams *) lParam;\r
7926     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7927     SetWindowText(hDlg, qp->title);\r
7928     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7929     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7930     return FALSE;\r
7931 \r
7932   case WM_COMMAND:\r
7933     switch (LOWORD(wParam)) {\r
7934     case IDOK:\r
7935       strcpy(reply, qp->replyPrefix);\r
7936       if (*reply) strcat(reply, " ");\r
7937       len = strlen(reply);\r
7938       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7939       strcat(reply, "\n");\r
7940       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7941       EndDialog(hDlg, TRUE);\r
7942       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7943       return TRUE;\r
7944     case IDCANCEL:\r
7945       EndDialog(hDlg, FALSE);\r
7946       return TRUE;\r
7947     default:\r
7948       break;\r
7949     }\r
7950     break;\r
7951   }\r
7952   return FALSE;\r
7953 }\r
7954 \r
7955 VOID\r
7956 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7957 {\r
7958     QuestionParams qp;\r
7959     FARPROC lpProc;\r
7960     \r
7961     qp.title = title;\r
7962     qp.question = question;\r
7963     qp.replyPrefix = replyPrefix;\r
7964     qp.pr = pr;\r
7965     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7966     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7967       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7968     FreeProcInstance(lpProc);\r
7969 }\r
7970 \r
7971 /* [AS] Pick FRC position */\r
7972 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7973 {\r
7974     static int * lpIndexFRC;\r
7975     BOOL index_is_ok;\r
7976     char buf[16];\r
7977 \r
7978     switch( message )\r
7979     {\r
7980     case WM_INITDIALOG:\r
7981         lpIndexFRC = (int *) lParam;\r
7982 \r
7983         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7984 \r
7985         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
7986         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
7987         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
7988         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
7989 \r
7990         break;\r
7991 \r
7992     case WM_COMMAND:\r
7993         switch( LOWORD(wParam) ) {\r
7994         case IDOK:\r
7995             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
7996             EndDialog( hDlg, 0 );\r
7997             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
7998             return TRUE;\r
7999         case IDCANCEL:\r
8000             EndDialog( hDlg, 1 );   \r
8001             return TRUE;\r
8002         case IDC_NFG_Edit:\r
8003             if( HIWORD(wParam) == EN_CHANGE ) {\r
8004                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8005 \r
8006                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8007             }\r
8008             return TRUE;\r
8009         case IDC_NFG_Random:\r
8010             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8011             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8012             return TRUE;\r
8013         }\r
8014 \r
8015         break;\r
8016     }\r
8017 \r
8018     return FALSE;\r
8019 }\r
8020 \r
8021 int NewGameFRC()\r
8022 {\r
8023     int result;\r
8024     int index = appData.defaultFrcPosition;\r
8025     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8026 \r
8027     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8028 \r
8029     if( result == 0 ) {\r
8030         appData.defaultFrcPosition = index;\r
8031     }\r
8032 \r
8033     return result;\r
8034 }\r
8035 \r
8036 /* [AS] Game list options. Refactored by HGM */\r
8037 \r
8038 HWND gameListOptionsDialog;\r
8039 \r
8040 // low-level front-end: clear text edit / list widget\r
8041 void\r
8042 GLT_ClearList()\r
8043 {\r
8044     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8045 }\r
8046 \r
8047 // low-level front-end: clear text edit / list widget\r
8048 void\r
8049 GLT_DeSelectList()\r
8050 {\r
8051     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8052 }\r
8053 \r
8054 // low-level front-end: append line to text edit / list widget\r
8055 void\r
8056 GLT_AddToList( char *name )\r
8057 {\r
8058     if( name != 0 ) {\r
8059             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8060     }\r
8061 }\r
8062 \r
8063 // low-level front-end: get line from text edit / list widget\r
8064 Boolean\r
8065 GLT_GetFromList( int index, char *name )\r
8066 {\r
8067     if( name != 0 ) {\r
8068             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8069                 return TRUE;\r
8070     }\r
8071     return FALSE;\r
8072 }\r
8073 \r
8074 void GLT_MoveSelection( HWND hDlg, int delta )\r
8075 {\r
8076     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8077     int idx2 = idx1 + delta;\r
8078     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8079 \r
8080     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8081         char buf[128];\r
8082 \r
8083         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8084         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8085         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8086         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8087     }\r
8088 }\r
8089 \r
8090 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8091 {\r
8092     switch( message )\r
8093     {\r
8094     case WM_INITDIALOG:\r
8095         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8096         \r
8097         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8098 \r
8099         /* Initialize list */\r
8100         GLT_TagsToList( lpUserGLT );\r
8101 \r
8102         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8103 \r
8104         break;\r
8105 \r
8106     case WM_COMMAND:\r
8107         switch( LOWORD(wParam) ) {\r
8108         case IDOK:\r
8109             GLT_ParseList();\r
8110             EndDialog( hDlg, 0 );\r
8111             return TRUE;\r
8112         case IDCANCEL:\r
8113             EndDialog( hDlg, 1 );\r
8114             return TRUE;\r
8115 \r
8116         case IDC_GLT_Default:\r
8117             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8118             return TRUE;\r
8119 \r
8120         case IDC_GLT_Restore:\r
8121             GLT_TagsToList( appData.gameListTags );\r
8122             return TRUE;\r
8123 \r
8124         case IDC_GLT_Up:\r
8125             GLT_MoveSelection( hDlg, -1 );\r
8126             return TRUE;\r
8127 \r
8128         case IDC_GLT_Down:\r
8129             GLT_MoveSelection( hDlg, +1 );\r
8130             return TRUE;\r
8131         }\r
8132 \r
8133         break;\r
8134     }\r
8135 \r
8136     return FALSE;\r
8137 }\r
8138 \r
8139 int GameListOptions()\r
8140 {\r
8141     int result;\r
8142     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8143 \r
8144     strcpy( lpUserGLT, appData.gameListTags );\r
8145 \r
8146     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8147 \r
8148     if( result == 0 ) {\r
8149         /* [AS] Memory leak here! */\r
8150         appData.gameListTags = strdup( lpUserGLT ); \r
8151     }\r
8152 \r
8153     return result;\r
8154 }\r
8155 \r
8156 VOID\r
8157 DisplayIcsInteractionTitle(char *str)\r
8158 {\r
8159   char consoleTitle[MSG_SIZ];\r
8160 \r
8161   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8162   SetWindowText(hwndConsole, consoleTitle);\r
8163 }\r
8164 \r
8165 void\r
8166 DrawPosition(int fullRedraw, Board board)\r
8167 {\r
8168   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8169 }\r
8170 \r
8171 void NotifyFrontendLogin()\r
8172 {\r
8173         if (hwndConsole)\r
8174                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8175 }\r
8176 \r
8177 VOID\r
8178 ResetFrontEnd()\r
8179 {\r
8180   fromX = fromY = -1;\r
8181   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8182     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8183     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8184     dragInfo.lastpos = dragInfo.pos;\r
8185     dragInfo.start.x = dragInfo.start.y = -1;\r
8186     dragInfo.from = dragInfo.start;\r
8187     ReleaseCapture();\r
8188     DrawPosition(TRUE, NULL);\r
8189   }\r
8190   TagsPopDown();\r
8191 }\r
8192 \r
8193 \r
8194 VOID\r
8195 CommentPopUp(char *title, char *str)\r
8196 {\r
8197   HWND hwnd = GetActiveWindow();\r
8198   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8199   SAY(str);\r
8200   SetActiveWindow(hwnd);\r
8201 }\r
8202 \r
8203 VOID\r
8204 CommentPopDown(void)\r
8205 {\r
8206   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8207   if (commentDialog) {\r
8208     ShowWindow(commentDialog, SW_HIDE);\r
8209   }\r
8210   commentUp = FALSE;\r
8211 }\r
8212 \r
8213 VOID\r
8214 EditCommentPopUp(int index, char *title, char *str)\r
8215 {\r
8216   EitherCommentPopUp(index, title, str, TRUE);\r
8217 }\r
8218 \r
8219 \r
8220 VOID\r
8221 RingBell()\r
8222 {\r
8223   MyPlaySound(&sounds[(int)SoundMove]);\r
8224 }\r
8225 \r
8226 VOID PlayIcsWinSound()\r
8227 {\r
8228   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8229 }\r
8230 \r
8231 VOID PlayIcsLossSound()\r
8232 {\r
8233   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8234 }\r
8235 \r
8236 VOID PlayIcsDrawSound()\r
8237 {\r
8238   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8239 }\r
8240 \r
8241 VOID PlayIcsUnfinishedSound()\r
8242 {\r
8243   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8244 }\r
8245 \r
8246 VOID\r
8247 PlayAlarmSound()\r
8248 {\r
8249   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8250 }\r
8251 \r
8252 \r
8253 VOID\r
8254 EchoOn()\r
8255 {\r
8256   HWND hInput;\r
8257   consoleEcho = TRUE;\r
8258   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8259   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8260   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8261 }\r
8262 \r
8263 \r
8264 VOID\r
8265 EchoOff()\r
8266 {\r
8267   CHARFORMAT cf;\r
8268   HWND hInput;\r
8269   consoleEcho = FALSE;\r
8270   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8271   /* This works OK: set text and background both to the same color */\r
8272   cf = consoleCF;\r
8273   cf.crTextColor = COLOR_ECHOOFF;\r
8274   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8275   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8276 }\r
8277 \r
8278 /* No Raw()...? */\r
8279 \r
8280 void Colorize(ColorClass cc, int continuation)\r
8281 {\r
8282   currentColorClass = cc;\r
8283   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8284   consoleCF.crTextColor = textAttribs[cc].color;\r
8285   consoleCF.dwEffects = textAttribs[cc].effects;\r
8286   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8287 }\r
8288 \r
8289 char *\r
8290 UserName()\r
8291 {\r
8292   static char buf[MSG_SIZ];\r
8293   DWORD bufsiz = MSG_SIZ;\r
8294 \r
8295   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8296         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8297   }\r
8298   if (!GetUserName(buf, &bufsiz)) {\r
8299     /*DisplayError("Error getting user name", GetLastError());*/\r
8300     strcpy(buf, "User");\r
8301   }\r
8302   return buf;\r
8303 }\r
8304 \r
8305 char *\r
8306 HostName()\r
8307 {\r
8308   static char buf[MSG_SIZ];\r
8309   DWORD bufsiz = MSG_SIZ;\r
8310 \r
8311   if (!GetComputerName(buf, &bufsiz)) {\r
8312     /*DisplayError("Error getting host name", GetLastError());*/\r
8313     strcpy(buf, "Unknown");\r
8314   }\r
8315   return buf;\r
8316 }\r
8317 \r
8318 \r
8319 int\r
8320 ClockTimerRunning()\r
8321 {\r
8322   return clockTimerEvent != 0;\r
8323 }\r
8324 \r
8325 int\r
8326 StopClockTimer()\r
8327 {\r
8328   if (clockTimerEvent == 0) return FALSE;\r
8329   KillTimer(hwndMain, clockTimerEvent);\r
8330   clockTimerEvent = 0;\r
8331   return TRUE;\r
8332 }\r
8333 \r
8334 void\r
8335 StartClockTimer(long millisec)\r
8336 {\r
8337   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8338                              (UINT) millisec, NULL);\r
8339 }\r
8340 \r
8341 void\r
8342 DisplayWhiteClock(long timeRemaining, int highlight)\r
8343 {\r
8344   HDC hdc;\r
8345   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8346 \r
8347   if(appData.noGUI) return;\r
8348   hdc = GetDC(hwndMain);\r
8349   if (!IsIconic(hwndMain)) {\r
8350     DisplayAClock(hdc, timeRemaining, highlight, \r
8351                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
8352   }\r
8353   if (highlight && iconCurrent == iconBlack) {\r
8354     iconCurrent = iconWhite;\r
8355     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8356     if (IsIconic(hwndMain)) {\r
8357       DrawIcon(hdc, 2, 2, iconCurrent);\r
8358     }\r
8359   }\r
8360   (void) ReleaseDC(hwndMain, hdc);\r
8361   if (hwndConsole)\r
8362     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8363 }\r
8364 \r
8365 void\r
8366 DisplayBlackClock(long timeRemaining, int highlight)\r
8367 {\r
8368   HDC hdc;\r
8369   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8370 \r
8371   if(appData.noGUI) return;\r
8372   hdc = GetDC(hwndMain);\r
8373   if (!IsIconic(hwndMain)) {\r
8374     DisplayAClock(hdc, timeRemaining, highlight, \r
8375                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8376   }\r
8377   if (highlight && iconCurrent == iconWhite) {\r
8378     iconCurrent = iconBlack;\r
8379     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8380     if (IsIconic(hwndMain)) {\r
8381       DrawIcon(hdc, 2, 2, iconCurrent);\r
8382     }\r
8383   }\r
8384   (void) ReleaseDC(hwndMain, hdc);\r
8385   if (hwndConsole)\r
8386     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8387 }\r
8388 \r
8389 \r
8390 int\r
8391 LoadGameTimerRunning()\r
8392 {\r
8393   return loadGameTimerEvent != 0;\r
8394 }\r
8395 \r
8396 int\r
8397 StopLoadGameTimer()\r
8398 {\r
8399   if (loadGameTimerEvent == 0) return FALSE;\r
8400   KillTimer(hwndMain, loadGameTimerEvent);\r
8401   loadGameTimerEvent = 0;\r
8402   return TRUE;\r
8403 }\r
8404 \r
8405 void\r
8406 StartLoadGameTimer(long millisec)\r
8407 {\r
8408   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8409                                 (UINT) millisec, NULL);\r
8410 }\r
8411 \r
8412 void\r
8413 AutoSaveGame()\r
8414 {\r
8415   char *defName;\r
8416   FILE *f;\r
8417   char fileTitle[MSG_SIZ];\r
8418 \r
8419   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8420   f = OpenFileDialog(hwndMain, "a", defName,\r
8421                      appData.oldSaveStyle ? "gam" : "pgn",\r
8422                      GAME_FILT, \r
8423                      "Save Game to File", NULL, fileTitle, NULL);\r
8424   if (f != NULL) {\r
8425     SaveGame(f, 0, "");\r
8426     fclose(f);\r
8427   }\r
8428 }\r
8429 \r
8430 \r
8431 void\r
8432 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8433 {\r
8434   if (delayedTimerEvent != 0) {\r
8435     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8436       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8437     }\r
8438     KillTimer(hwndMain, delayedTimerEvent);\r
8439     delayedTimerEvent = 0;\r
8440     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8441     delayedTimerCallback();\r
8442   }\r
8443   delayedTimerCallback = cb;\r
8444   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8445                                 (UINT) millisec, NULL);\r
8446 }\r
8447 \r
8448 DelayedEventCallback\r
8449 GetDelayedEvent()\r
8450 {\r
8451   if (delayedTimerEvent) {\r
8452     return delayedTimerCallback;\r
8453   } else {\r
8454     return NULL;\r
8455   }\r
8456 }\r
8457 \r
8458 void\r
8459 CancelDelayedEvent()\r
8460 {\r
8461   if (delayedTimerEvent) {\r
8462     KillTimer(hwndMain, delayedTimerEvent);\r
8463     delayedTimerEvent = 0;\r
8464   }\r
8465 }\r
8466 \r
8467 DWORD GetWin32Priority(int nice)\r
8468 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8469 /*\r
8470 REALTIME_PRIORITY_CLASS     0x00000100\r
8471 HIGH_PRIORITY_CLASS         0x00000080\r
8472 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8473 NORMAL_PRIORITY_CLASS       0x00000020\r
8474 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8475 IDLE_PRIORITY_CLASS         0x00000040\r
8476 */\r
8477         if (nice < -15) return 0x00000080;\r
8478         if (nice < 0)   return 0x00008000;\r
8479         if (nice == 0)  return 0x00000020;\r
8480         if (nice < 15)  return 0x00004000;\r
8481         return 0x00000040;\r
8482 }\r
8483 \r
8484 /* Start a child process running the given program.\r
8485    The process's standard output can be read from "from", and its\r
8486    standard input can be written to "to".\r
8487    Exit with fatal error if anything goes wrong.\r
8488    Returns an opaque pointer that can be used to destroy the process\r
8489    later.\r
8490 */\r
8491 int\r
8492 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8493 {\r
8494 #define BUFSIZE 4096\r
8495 \r
8496   HANDLE hChildStdinRd, hChildStdinWr,\r
8497     hChildStdoutRd, hChildStdoutWr;\r
8498   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8499   SECURITY_ATTRIBUTES saAttr;\r
8500   BOOL fSuccess;\r
8501   PROCESS_INFORMATION piProcInfo;\r
8502   STARTUPINFO siStartInfo;\r
8503   ChildProc *cp;\r
8504   char buf[MSG_SIZ];\r
8505   DWORD err;\r
8506 \r
8507   if (appData.debugMode) {\r
8508     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8509   }\r
8510 \r
8511   *pr = NoProc;\r
8512 \r
8513   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8514   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8515   saAttr.bInheritHandle = TRUE;\r
8516   saAttr.lpSecurityDescriptor = NULL;\r
8517 \r
8518   /*\r
8519    * The steps for redirecting child's STDOUT:\r
8520    *     1. Create anonymous pipe to be STDOUT for child.\r
8521    *     2. Create a noninheritable duplicate of read handle,\r
8522    *         and close the inheritable read handle.\r
8523    */\r
8524 \r
8525   /* Create a pipe for the child's STDOUT. */\r
8526   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8527     return GetLastError();\r
8528   }\r
8529 \r
8530   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8531   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8532                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8533                              FALSE,     /* not inherited */\r
8534                              DUPLICATE_SAME_ACCESS);\r
8535   if (! fSuccess) {\r
8536     return GetLastError();\r
8537   }\r
8538   CloseHandle(hChildStdoutRd);\r
8539 \r
8540   /*\r
8541    * The steps for redirecting child's STDIN:\r
8542    *     1. Create anonymous pipe to be STDIN for child.\r
8543    *     2. Create a noninheritable duplicate of write handle,\r
8544    *         and close the inheritable write handle.\r
8545    */\r
8546 \r
8547   /* Create a pipe for the child's STDIN. */\r
8548   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8549     return GetLastError();\r
8550   }\r
8551 \r
8552   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8553   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8554                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8555                              FALSE,     /* not inherited */\r
8556                              DUPLICATE_SAME_ACCESS);\r
8557   if (! fSuccess) {\r
8558     return GetLastError();\r
8559   }\r
8560   CloseHandle(hChildStdinWr);\r
8561 \r
8562   /* Arrange to (1) look in dir for the child .exe file, and\r
8563    * (2) have dir be the child's working directory.  Interpret\r
8564    * dir relative to the directory WinBoard loaded from. */\r
8565   GetCurrentDirectory(MSG_SIZ, buf);\r
8566   SetCurrentDirectory(installDir);\r
8567   SetCurrentDirectory(dir);\r
8568 \r
8569   /* Now create the child process. */\r
8570 \r
8571   siStartInfo.cb = sizeof(STARTUPINFO);\r
8572   siStartInfo.lpReserved = NULL;\r
8573   siStartInfo.lpDesktop = NULL;\r
8574   siStartInfo.lpTitle = NULL;\r
8575   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8576   siStartInfo.cbReserved2 = 0;\r
8577   siStartInfo.lpReserved2 = NULL;\r
8578   siStartInfo.hStdInput = hChildStdinRd;\r
8579   siStartInfo.hStdOutput = hChildStdoutWr;\r
8580   siStartInfo.hStdError = hChildStdoutWr;\r
8581 \r
8582   fSuccess = CreateProcess(NULL,\r
8583                            cmdLine,        /* command line */\r
8584                            NULL,           /* process security attributes */\r
8585                            NULL,           /* primary thread security attrs */\r
8586                            TRUE,           /* handles are inherited */\r
8587                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8588                            NULL,           /* use parent's environment */\r
8589                            NULL,\r
8590                            &siStartInfo, /* STARTUPINFO pointer */\r
8591                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8592 \r
8593   err = GetLastError();\r
8594   SetCurrentDirectory(buf); /* return to prev directory */\r
8595   if (! fSuccess) {\r
8596     return err;\r
8597   }\r
8598 \r
8599   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8600     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8601     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8602   }\r
8603 \r
8604   /* Close the handles we don't need in the parent */\r
8605   CloseHandle(piProcInfo.hThread);\r
8606   CloseHandle(hChildStdinRd);\r
8607   CloseHandle(hChildStdoutWr);\r
8608 \r
8609   /* Prepare return value */\r
8610   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8611   cp->kind = CPReal;\r
8612   cp->hProcess = piProcInfo.hProcess;\r
8613   cp->pid = piProcInfo.dwProcessId;\r
8614   cp->hFrom = hChildStdoutRdDup;\r
8615   cp->hTo = hChildStdinWrDup;\r
8616 \r
8617   *pr = (void *) cp;\r
8618 \r
8619   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8620      2000 where engines sometimes don't see the initial command(s)\r
8621      from WinBoard and hang.  I don't understand how that can happen,\r
8622      but the Sleep is harmless, so I've put it in.  Others have also\r
8623      reported what may be the same problem, so hopefully this will fix\r
8624      it for them too.  */\r
8625   Sleep(500);\r
8626 \r
8627   return NO_ERROR;\r
8628 }\r
8629 \r
8630 \r
8631 void\r
8632 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8633 {\r
8634   ChildProc *cp; int result;\r
8635 \r
8636   cp = (ChildProc *) pr;\r
8637   if (cp == NULL) return;\r
8638 \r
8639   switch (cp->kind) {\r
8640   case CPReal:\r
8641     /* TerminateProcess is considered harmful, so... */\r
8642     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8643     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8644     /* The following doesn't work because the chess program\r
8645        doesn't "have the same console" as WinBoard.  Maybe\r
8646        we could arrange for this even though neither WinBoard\r
8647        nor the chess program uses a console for stdio? */\r
8648     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8649 \r
8650     /* [AS] Special termination modes for misbehaving programs... */\r
8651     if( signal == 9 ) { \r
8652         result = TerminateProcess( cp->hProcess, 0 );\r
8653 \r
8654         if ( appData.debugMode) {\r
8655             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8656         }\r
8657     }\r
8658     else if( signal == 10 ) {\r
8659         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8660 \r
8661         if( dw != WAIT_OBJECT_0 ) {\r
8662             result = TerminateProcess( cp->hProcess, 0 );\r
8663 \r
8664             if ( appData.debugMode) {\r
8665                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8666             }\r
8667 \r
8668         }\r
8669     }\r
8670 \r
8671     CloseHandle(cp->hProcess);\r
8672     break;\r
8673 \r
8674   case CPComm:\r
8675     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8676     break;\r
8677 \r
8678   case CPSock:\r
8679     closesocket(cp->sock);\r
8680     WSACleanup();\r
8681     break;\r
8682 \r
8683   case CPRcmd:\r
8684     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8685     closesocket(cp->sock);\r
8686     closesocket(cp->sock2);\r
8687     WSACleanup();\r
8688     break;\r
8689   }\r
8690   free(cp);\r
8691 }\r
8692 \r
8693 void\r
8694 InterruptChildProcess(ProcRef pr)\r
8695 {\r
8696   ChildProc *cp;\r
8697 \r
8698   cp = (ChildProc *) pr;\r
8699   if (cp == NULL) return;\r
8700   switch (cp->kind) {\r
8701   case CPReal:\r
8702     /* The following doesn't work because the chess program\r
8703        doesn't "have the same console" as WinBoard.  Maybe\r
8704        we could arrange for this even though neither WinBoard\r
8705        nor the chess program uses a console for stdio */\r
8706     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8707     break;\r
8708 \r
8709   case CPComm:\r
8710   case CPSock:\r
8711     /* Can't interrupt */\r
8712     break;\r
8713 \r
8714   case CPRcmd:\r
8715     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8716     break;\r
8717   }\r
8718 }\r
8719 \r
8720 \r
8721 int\r
8722 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8723 {\r
8724   char cmdLine[MSG_SIZ];\r
8725 \r
8726   if (port[0] == NULLCHAR) {\r
8727     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8728   } else {\r
8729     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8730   }\r
8731   return StartChildProcess(cmdLine, "", pr);\r
8732 }\r
8733 \r
8734 \r
8735 /* Code to open TCP sockets */\r
8736 \r
8737 int\r
8738 OpenTCP(char *host, char *port, ProcRef *pr)\r
8739 {\r
8740   ChildProc *cp;\r
8741   int err;\r
8742   SOCKET s;\r
8743   struct sockaddr_in sa, mysa;\r
8744   struct hostent FAR *hp;\r
8745   unsigned short uport;\r
8746   WORD wVersionRequested;\r
8747   WSADATA wsaData;\r
8748 \r
8749   /* Initialize socket DLL */\r
8750   wVersionRequested = MAKEWORD(1, 1);\r
8751   err = WSAStartup(wVersionRequested, &wsaData);\r
8752   if (err != 0) return err;\r
8753 \r
8754   /* Make socket */\r
8755   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8756     err = WSAGetLastError();\r
8757     WSACleanup();\r
8758     return err;\r
8759   }\r
8760 \r
8761   /* Bind local address using (mostly) don't-care values.\r
8762    */\r
8763   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8764   mysa.sin_family = AF_INET;\r
8765   mysa.sin_addr.s_addr = INADDR_ANY;\r
8766   uport = (unsigned short) 0;\r
8767   mysa.sin_port = htons(uport);\r
8768   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8769       == SOCKET_ERROR) {\r
8770     err = WSAGetLastError();\r
8771     WSACleanup();\r
8772     return err;\r
8773   }\r
8774 \r
8775   /* Resolve remote host name */\r
8776   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8777   if (!(hp = gethostbyname(host))) {\r
8778     unsigned int b0, b1, b2, b3;\r
8779 \r
8780     err = WSAGetLastError();\r
8781 \r
8782     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8783       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8784       hp->h_addrtype = AF_INET;\r
8785       hp->h_length = 4;\r
8786       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8787       hp->h_addr_list[0] = (char *) malloc(4);\r
8788       hp->h_addr_list[0][0] = (char) b0;\r
8789       hp->h_addr_list[0][1] = (char) b1;\r
8790       hp->h_addr_list[0][2] = (char) b2;\r
8791       hp->h_addr_list[0][3] = (char) b3;\r
8792     } else {\r
8793       WSACleanup();\r
8794       return err;\r
8795     }\r
8796   }\r
8797   sa.sin_family = hp->h_addrtype;\r
8798   uport = (unsigned short) atoi(port);\r
8799   sa.sin_port = htons(uport);\r
8800   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8801 \r
8802   /* Make connection */\r
8803   if (connect(s, (struct sockaddr *) &sa,\r
8804               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8805     err = WSAGetLastError();\r
8806     WSACleanup();\r
8807     return err;\r
8808   }\r
8809 \r
8810   /* Prepare return value */\r
8811   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8812   cp->kind = CPSock;\r
8813   cp->sock = s;\r
8814   *pr = (ProcRef *) cp;\r
8815 \r
8816   return NO_ERROR;\r
8817 }\r
8818 \r
8819 int\r
8820 OpenCommPort(char *name, ProcRef *pr)\r
8821 {\r
8822   HANDLE h;\r
8823   COMMTIMEOUTS ct;\r
8824   ChildProc *cp;\r
8825   char fullname[MSG_SIZ];\r
8826 \r
8827   if (*name != '\\')\r
8828     sprintf(fullname, "\\\\.\\%s", name);\r
8829   else\r
8830     strcpy(fullname, name);\r
8831 \r
8832   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8833                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8834   if (h == (HANDLE) -1) {\r
8835     return GetLastError();\r
8836   }\r
8837   hCommPort = h;\r
8838 \r
8839   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8840 \r
8841   /* Accumulate characters until a 100ms pause, then parse */\r
8842   ct.ReadIntervalTimeout = 100;\r
8843   ct.ReadTotalTimeoutMultiplier = 0;\r
8844   ct.ReadTotalTimeoutConstant = 0;\r
8845   ct.WriteTotalTimeoutMultiplier = 0;\r
8846   ct.WriteTotalTimeoutConstant = 0;\r
8847   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8848 \r
8849   /* Prepare return value */\r
8850   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8851   cp->kind = CPComm;\r
8852   cp->hFrom = h;\r
8853   cp->hTo = h;\r
8854   *pr = (ProcRef *) cp;\r
8855 \r
8856   return NO_ERROR;\r
8857 }\r
8858 \r
8859 int\r
8860 OpenLoopback(ProcRef *pr)\r
8861 {\r
8862   DisplayFatalError("Not implemented", 0, 1);\r
8863   return NO_ERROR;\r
8864 }\r
8865 \r
8866 \r
8867 int\r
8868 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8869 {\r
8870   ChildProc *cp;\r
8871   int err;\r
8872   SOCKET s, s2, s3;\r
8873   struct sockaddr_in sa, mysa;\r
8874   struct hostent FAR *hp;\r
8875   unsigned short uport;\r
8876   WORD wVersionRequested;\r
8877   WSADATA wsaData;\r
8878   int fromPort;\r
8879   char stderrPortStr[MSG_SIZ];\r
8880 \r
8881   /* Initialize socket DLL */\r
8882   wVersionRequested = MAKEWORD(1, 1);\r
8883   err = WSAStartup(wVersionRequested, &wsaData);\r
8884   if (err != 0) return err;\r
8885 \r
8886   /* Resolve remote host name */\r
8887   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8888   if (!(hp = gethostbyname(host))) {\r
8889     unsigned int b0, b1, b2, b3;\r
8890 \r
8891     err = WSAGetLastError();\r
8892 \r
8893     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8894       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8895       hp->h_addrtype = AF_INET;\r
8896       hp->h_length = 4;\r
8897       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8898       hp->h_addr_list[0] = (char *) malloc(4);\r
8899       hp->h_addr_list[0][0] = (char) b0;\r
8900       hp->h_addr_list[0][1] = (char) b1;\r
8901       hp->h_addr_list[0][2] = (char) b2;\r
8902       hp->h_addr_list[0][3] = (char) b3;\r
8903     } else {\r
8904       WSACleanup();\r
8905       return err;\r
8906     }\r
8907   }\r
8908   sa.sin_family = hp->h_addrtype;\r
8909   uport = (unsigned short) 514;\r
8910   sa.sin_port = htons(uport);\r
8911   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8912 \r
8913   /* Bind local socket to unused "privileged" port address\r
8914    */\r
8915   s = INVALID_SOCKET;\r
8916   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8917   mysa.sin_family = AF_INET;\r
8918   mysa.sin_addr.s_addr = INADDR_ANY;\r
8919   for (fromPort = 1023;; fromPort--) {\r
8920     if (fromPort < 0) {\r
8921       WSACleanup();\r
8922       return WSAEADDRINUSE;\r
8923     }\r
8924     if (s == INVALID_SOCKET) {\r
8925       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8926         err = WSAGetLastError();\r
8927         WSACleanup();\r
8928         return err;\r
8929       }\r
8930     }\r
8931     uport = (unsigned short) fromPort;\r
8932     mysa.sin_port = htons(uport);\r
8933     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8934         == SOCKET_ERROR) {\r
8935       err = WSAGetLastError();\r
8936       if (err == WSAEADDRINUSE) continue;\r
8937       WSACleanup();\r
8938       return err;\r
8939     }\r
8940     if (connect(s, (struct sockaddr *) &sa,\r
8941       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8942       err = WSAGetLastError();\r
8943       if (err == WSAEADDRINUSE) {\r
8944         closesocket(s);\r
8945         s = -1;\r
8946         continue;\r
8947       }\r
8948       WSACleanup();\r
8949       return err;\r
8950     }\r
8951     break;\r
8952   }\r
8953 \r
8954   /* Bind stderr local socket to unused "privileged" port address\r
8955    */\r
8956   s2 = INVALID_SOCKET;\r
8957   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8958   mysa.sin_family = AF_INET;\r
8959   mysa.sin_addr.s_addr = INADDR_ANY;\r
8960   for (fromPort = 1023;; fromPort--) {\r
8961     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8962     if (fromPort < 0) {\r
8963       (void) closesocket(s);\r
8964       WSACleanup();\r
8965       return WSAEADDRINUSE;\r
8966     }\r
8967     if (s2 == INVALID_SOCKET) {\r
8968       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8969         err = WSAGetLastError();\r
8970         closesocket(s);\r
8971         WSACleanup();\r
8972         return err;\r
8973       }\r
8974     }\r
8975     uport = (unsigned short) fromPort;\r
8976     mysa.sin_port = htons(uport);\r
8977     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8978         == SOCKET_ERROR) {\r
8979       err = WSAGetLastError();\r
8980       if (err == WSAEADDRINUSE) continue;\r
8981       (void) closesocket(s);\r
8982       WSACleanup();\r
8983       return err;\r
8984     }\r
8985     if (listen(s2, 1) == SOCKET_ERROR) {\r
8986       err = WSAGetLastError();\r
8987       if (err == WSAEADDRINUSE) {\r
8988         closesocket(s2);\r
8989         s2 = INVALID_SOCKET;\r
8990         continue;\r
8991       }\r
8992       (void) closesocket(s);\r
8993       (void) closesocket(s2);\r
8994       WSACleanup();\r
8995       return err;\r
8996     }\r
8997     break;\r
8998   }\r
8999   prevStderrPort = fromPort; // remember port used\r
9000   sprintf(stderrPortStr, "%d", fromPort);\r
9001 \r
9002   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9003     err = WSAGetLastError();\r
9004     (void) closesocket(s);\r
9005     (void) closesocket(s2);\r
9006     WSACleanup();\r
9007     return err;\r
9008   }\r
9009 \r
9010   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9011     err = WSAGetLastError();\r
9012     (void) closesocket(s);\r
9013     (void) closesocket(s2);\r
9014     WSACleanup();\r
9015     return err;\r
9016   }\r
9017   if (*user == NULLCHAR) user = UserName();\r
9018   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9019     err = WSAGetLastError();\r
9020     (void) closesocket(s);\r
9021     (void) closesocket(s2);\r
9022     WSACleanup();\r
9023     return err;\r
9024   }\r
9025   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9026     err = WSAGetLastError();\r
9027     (void) closesocket(s);\r
9028     (void) closesocket(s2);\r
9029     WSACleanup();\r
9030     return err;\r
9031   }\r
9032 \r
9033   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9034     err = WSAGetLastError();\r
9035     (void) closesocket(s);\r
9036     (void) closesocket(s2);\r
9037     WSACleanup();\r
9038     return err;\r
9039   }\r
9040   (void) closesocket(s2);  /* Stop listening */\r
9041 \r
9042   /* Prepare return value */\r
9043   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9044   cp->kind = CPRcmd;\r
9045   cp->sock = s;\r
9046   cp->sock2 = s3;\r
9047   *pr = (ProcRef *) cp;\r
9048 \r
9049   return NO_ERROR;\r
9050 }\r
9051 \r
9052 \r
9053 InputSourceRef\r
9054 AddInputSource(ProcRef pr, int lineByLine,\r
9055                InputCallback func, VOIDSTAR closure)\r
9056 {\r
9057   InputSource *is, *is2 = NULL;\r
9058   ChildProc *cp = (ChildProc *) pr;\r
9059 \r
9060   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9061   is->lineByLine = lineByLine;\r
9062   is->func = func;\r
9063   is->closure = closure;\r
9064   is->second = NULL;\r
9065   is->next = is->buf;\r
9066   if (pr == NoProc) {\r
9067     is->kind = CPReal;\r
9068     consoleInputSource = is;\r
9069   } else {\r
9070     is->kind = cp->kind;\r
9071     /* \r
9072         [AS] Try to avoid a race condition if the thread is given control too early:\r
9073         we create all threads suspended so that the is->hThread variable can be\r
9074         safely assigned, then let the threads start with ResumeThread.\r
9075     */\r
9076     switch (cp->kind) {\r
9077     case CPReal:\r
9078       is->hFile = cp->hFrom;\r
9079       cp->hFrom = NULL; /* now owned by InputThread */\r
9080       is->hThread =\r
9081         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9082                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9083       break;\r
9084 \r
9085     case CPComm:\r
9086       is->hFile = cp->hFrom;\r
9087       cp->hFrom = NULL; /* now owned by InputThread */\r
9088       is->hThread =\r
9089         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9090                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9091       break;\r
9092 \r
9093     case CPSock:\r
9094       is->sock = cp->sock;\r
9095       is->hThread =\r
9096         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9097                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9098       break;\r
9099 \r
9100     case CPRcmd:\r
9101       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9102       *is2 = *is;\r
9103       is->sock = cp->sock;\r
9104       is->second = is2;\r
9105       is2->sock = cp->sock2;\r
9106       is2->second = is2;\r
9107       is->hThread =\r
9108         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9109                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9110       is2->hThread =\r
9111         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9112                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9113       break;\r
9114     }\r
9115 \r
9116     if( is->hThread != NULL ) {\r
9117         ResumeThread( is->hThread );\r
9118     }\r
9119 \r
9120     if( is2 != NULL && is2->hThread != NULL ) {\r
9121         ResumeThread( is2->hThread );\r
9122     }\r
9123   }\r
9124 \r
9125   return (InputSourceRef) is;\r
9126 }\r
9127 \r
9128 void\r
9129 RemoveInputSource(InputSourceRef isr)\r
9130 {\r
9131   InputSource *is;\r
9132 \r
9133   is = (InputSource *) isr;\r
9134   is->hThread = NULL;  /* tell thread to stop */\r
9135   CloseHandle(is->hThread);\r
9136   if (is->second != NULL) {\r
9137     is->second->hThread = NULL;\r
9138     CloseHandle(is->second->hThread);\r
9139   }\r
9140 }\r
9141 \r
9142 int no_wrap(char *message, int count)\r
9143 {\r
9144     ConsoleOutput(message, count, FALSE);\r
9145     return count;\r
9146 }\r
9147 \r
9148 int\r
9149 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9150 {\r
9151   DWORD dOutCount;\r
9152   int outCount = SOCKET_ERROR;\r
9153   ChildProc *cp = (ChildProc *) pr;\r
9154   static OVERLAPPED ovl;\r
9155   static int line = 0;\r
9156 \r
9157   if (pr == NoProc)\r
9158   {\r
9159     if (appData.noJoin || !appData.useInternalWrap)\r
9160       return no_wrap(message, count);\r
9161     else\r
9162     {\r
9163       int width = get_term_width();\r
9164       int len = wrap(NULL, message, count, width, &line);\r
9165       char *msg = malloc(len);\r
9166       int dbgchk;\r
9167 \r
9168       if (!msg)\r
9169         return no_wrap(message, count);\r
9170       else\r
9171       {\r
9172         dbgchk = wrap(msg, message, count, width, &line);\r
9173         if (dbgchk != len && appData.debugMode)\r
9174             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9175         ConsoleOutput(msg, len, FALSE);\r
9176         free(msg);\r
9177         return len;\r
9178       }\r
9179     }\r
9180   }\r
9181 \r
9182   if (ovl.hEvent == NULL) {\r
9183     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9184   }\r
9185   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9186 \r
9187   switch (cp->kind) {\r
9188   case CPSock:\r
9189   case CPRcmd:\r
9190     outCount = send(cp->sock, message, count, 0);\r
9191     if (outCount == SOCKET_ERROR) {\r
9192       *outError = WSAGetLastError();\r
9193     } else {\r
9194       *outError = NO_ERROR;\r
9195     }\r
9196     break;\r
9197 \r
9198   case CPReal:\r
9199     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9200                   &dOutCount, NULL)) {\r
9201       *outError = NO_ERROR;\r
9202       outCount = (int) dOutCount;\r
9203     } else {\r
9204       *outError = GetLastError();\r
9205     }\r
9206     break;\r
9207 \r
9208   case CPComm:\r
9209     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9210                             &dOutCount, &ovl);\r
9211     if (*outError == NO_ERROR) {\r
9212       outCount = (int) dOutCount;\r
9213     }\r
9214     break;\r
9215   }\r
9216   return outCount;\r
9217 }\r
9218 \r
9219 int\r
9220 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9221                        long msdelay)\r
9222 {\r
9223   /* Ignore delay, not implemented for WinBoard */\r
9224   return OutputToProcess(pr, message, count, outError);\r
9225 }\r
9226 \r
9227 \r
9228 void\r
9229 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9230                         char *buf, int count, int error)\r
9231 {\r
9232   DisplayFatalError("Not implemented", 0, 1);\r
9233 }\r
9234 \r
9235 /* see wgamelist.c for Game List functions */\r
9236 /* see wedittags.c for Edit Tags functions */\r
9237 \r
9238 \r
9239 VOID\r
9240 ICSInitScript()\r
9241 {\r
9242   FILE *f;\r
9243   char buf[MSG_SIZ];\r
9244   char *dummy;\r
9245 \r
9246   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9247     f = fopen(buf, "r");\r
9248     if (f != NULL) {\r
9249       ProcessICSInitScript(f);\r
9250       fclose(f);\r
9251     }\r
9252   }\r
9253 }\r
9254 \r
9255 \r
9256 VOID\r
9257 StartAnalysisClock()\r
9258 {\r
9259   if (analysisTimerEvent) return;\r
9260   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9261                                         (UINT) 2000, NULL);\r
9262 }\r
9263 \r
9264 VOID\r
9265 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9266 {\r
9267   highlightInfo.sq[0].x = fromX;\r
9268   highlightInfo.sq[0].y = fromY;\r
9269   highlightInfo.sq[1].x = toX;\r
9270   highlightInfo.sq[1].y = toY;\r
9271 }\r
9272 \r
9273 VOID\r
9274 ClearHighlights()\r
9275 {\r
9276   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9277     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9278 }\r
9279 \r
9280 VOID\r
9281 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9282 {\r
9283   premoveHighlightInfo.sq[0].x = fromX;\r
9284   premoveHighlightInfo.sq[0].y = fromY;\r
9285   premoveHighlightInfo.sq[1].x = toX;\r
9286   premoveHighlightInfo.sq[1].y = toY;\r
9287 }\r
9288 \r
9289 VOID\r
9290 ClearPremoveHighlights()\r
9291 {\r
9292   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9293     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9294 }\r
9295 \r
9296 VOID\r
9297 ShutDownFrontEnd()\r
9298 {\r
9299   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9300   DeleteClipboardTempFiles();\r
9301 }\r
9302 \r
9303 void\r
9304 BoardToTop()\r
9305 {\r
9306     if (IsIconic(hwndMain))\r
9307       ShowWindow(hwndMain, SW_RESTORE);\r
9308 \r
9309     SetActiveWindow(hwndMain);\r
9310 }\r
9311 \r
9312 /*\r
9313  * Prototypes for animation support routines\r
9314  */\r
9315 static void ScreenSquare(int column, int row, POINT * pt);\r
9316 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9317      POINT frames[], int * nFrames);\r
9318 \r
9319 \r
9320 void\r
9321 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9322 {       // [HGM] atomic: animate blast wave\r
9323         int i;\r
9324 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9325         explodeInfo.fromX = fromX;\r
9326         explodeInfo.fromY = fromY;\r
9327         explodeInfo.toX = toX;\r
9328         explodeInfo.toY = toY;\r
9329         for(i=1; i<nFrames; i++) {\r
9330             explodeInfo.radius = (i*180)/(nFrames-1);\r
9331             DrawPosition(FALSE, NULL);\r
9332             Sleep(appData.animSpeed);\r
9333         }\r
9334         explodeInfo.radius = 0;\r
9335         DrawPosition(TRUE, NULL);\r
9336 }\r
9337 \r
9338 #define kFactor 4\r
9339 \r
9340 void\r
9341 AnimateMove(board, fromX, fromY, toX, toY)\r
9342      Board board;\r
9343      int fromX;\r
9344      int fromY;\r
9345      int toX;\r
9346      int toY;\r
9347 {\r
9348   ChessSquare piece;\r
9349   POINT start, finish, mid;\r
9350   POINT frames[kFactor * 2 + 1];\r
9351   int nFrames, n;\r
9352 \r
9353   if (!appData.animate) return;\r
9354   if (doingSizing) return;\r
9355   if (fromY < 0 || fromX < 0) return;\r
9356   piece = board[fromY][fromX];\r
9357   if (piece >= EmptySquare) return;\r
9358 \r
9359   ScreenSquare(fromX, fromY, &start);\r
9360   ScreenSquare(toX, toY, &finish);\r
9361 \r
9362   /* All pieces except knights move in straight line */\r
9363   if (piece != WhiteKnight && piece != BlackKnight) {\r
9364     mid.x = start.x + (finish.x - start.x) / 2;\r
9365     mid.y = start.y + (finish.y - start.y) / 2;\r
9366   } else {\r
9367     /* Knight: make diagonal movement then straight */\r
9368     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9369        mid.x = start.x + (finish.x - start.x) / 2;\r
9370        mid.y = finish.y;\r
9371      } else {\r
9372        mid.x = finish.x;\r
9373        mid.y = start.y + (finish.y - start.y) / 2;\r
9374      }\r
9375   }\r
9376   \r
9377   /* Don't use as many frames for very short moves */\r
9378   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9379     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9380   else\r
9381     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9382 \r
9383   animInfo.from.x = fromX;\r
9384   animInfo.from.y = fromY;\r
9385   animInfo.to.x = toX;\r
9386   animInfo.to.y = toY;\r
9387   animInfo.lastpos = start;\r
9388   animInfo.piece = piece;\r
9389   for (n = 0; n < nFrames; n++) {\r
9390     animInfo.pos = frames[n];\r
9391     DrawPosition(FALSE, NULL);\r
9392     animInfo.lastpos = animInfo.pos;\r
9393     Sleep(appData.animSpeed);\r
9394   }\r
9395   animInfo.pos = finish;\r
9396   DrawPosition(FALSE, NULL);\r
9397   animInfo.piece = EmptySquare;\r
9398   if(gameInfo.variant == VariantAtomic && \r
9399      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9400         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9401 }\r
9402 \r
9403 /*      Convert board position to corner of screen rect and color       */\r
9404 \r
9405 static void\r
9406 ScreenSquare(column, row, pt)\r
9407      int column; int row; POINT * pt;\r
9408 {\r
9409   if (flipView) {\r
9410     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9411     pt->y = lineGap + row * (squareSize + lineGap);\r
9412   } else {\r
9413     pt->x = lineGap + column * (squareSize + lineGap);\r
9414     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9415   }\r
9416 }\r
9417 \r
9418 /*      Generate a series of frame coords from start->mid->finish.\r
9419         The movement rate doubles until the half way point is\r
9420         reached, then halves back down to the final destination,\r
9421         which gives a nice slow in/out effect. The algorithmn\r
9422         may seem to generate too many intermediates for short\r
9423         moves, but remember that the purpose is to attract the\r
9424         viewers attention to the piece about to be moved and\r
9425         then to where it ends up. Too few frames would be less\r
9426         noticeable.                                             */\r
9427 \r
9428 static void\r
9429 Tween(start, mid, finish, factor, frames, nFrames)\r
9430      POINT * start; POINT * mid;\r
9431      POINT * finish; int factor;\r
9432      POINT frames[]; int * nFrames;\r
9433 {\r
9434   int n, fraction = 1, count = 0;\r
9435 \r
9436   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9437   for (n = 0; n < factor; n++)\r
9438     fraction *= 2;\r
9439   for (n = 0; n < factor; n++) {\r
9440     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9441     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9442     count ++;\r
9443     fraction = fraction / 2;\r
9444   }\r
9445   \r
9446   /* Midpoint */\r
9447   frames[count] = *mid;\r
9448   count ++;\r
9449   \r
9450   /* Slow out, stepping 1/2, then 1/4, ... */\r
9451   fraction = 2;\r
9452   for (n = 0; n < factor; n++) {\r
9453     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9454     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9455     count ++;\r
9456     fraction = fraction * 2;\r
9457   }\r
9458   *nFrames = count;\r
9459 }\r
9460 \r
9461 void\r
9462 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9463 {\r
9464     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9465 \r
9466     EvalGraphSet( first, last, current, pvInfoList );\r
9467 }\r