Allow match to be started from WB menu
[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_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4559       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4560       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4561       appData.matchGames = appData.defaultMatchGames;\r
4562       matchGame = 1;\r
4563 \r
4564     case IDM_TwoMachines:\r
4565       TwoMachinesEvent();\r
4566       /*\r
4567        * refresh the tags dialog only if it's visible\r
4568        */\r
4569       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4570           char *tags;\r
4571           tags = PGNTags(&gameInfo);\r
4572           TagsPopUp(tags, CmailMsg());\r
4573           free(tags);\r
4574       }\r
4575       SAY("computer starts playing both sides");\r
4576       break;\r
4577 \r
4578     case IDM_AnalysisMode:\r
4579       if (!first.analysisSupport) {\r
4580         sprintf(buf, "%s does not support analysis", first.tidy);\r
4581         DisplayError(buf, 0);\r
4582       } else {\r
4583         SAY("analyzing current position");\r
4584         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4585         if (appData.icsActive) {\r
4586                if (gameMode != IcsObserving) {\r
4587                        sprintf(buf, "You are not observing a game");\r
4588                        DisplayError(buf, 0);\r
4589                        /* secure check */\r
4590                        if (appData.icsEngineAnalyze) {\r
4591                                if (appData.debugMode) \r
4592                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4593                                ExitAnalyzeMode();\r
4594                                ModeHighlight();\r
4595                                break;\r
4596                        }\r
4597                        break;\r
4598                } else {\r
4599                        /* if enable, user want disable icsEngineAnalyze */\r
4600                        if (appData.icsEngineAnalyze) {\r
4601                                ExitAnalyzeMode();\r
4602                                ModeHighlight();\r
4603                                break;\r
4604                        }\r
4605                        appData.icsEngineAnalyze = TRUE;\r
4606                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4607                }\r
4608         } \r
4609         if (!appData.showThinking) ToggleShowThinking();\r
4610         AnalyzeModeEvent();\r
4611       }\r
4612       break;\r
4613 \r
4614     case IDM_AnalyzeFile:\r
4615       if (!first.analysisSupport) {\r
4616         char buf[MSG_SIZ];\r
4617         sprintf(buf, "%s does not support analysis", first.tidy);\r
4618         DisplayError(buf, 0);\r
4619       } else {\r
4620         if (!appData.showThinking) ToggleShowThinking();\r
4621         AnalyzeFileEvent();\r
4622         LoadGameDialog(hwnd, "Analyze Game from File");\r
4623         AnalysisPeriodicEvent(1);\r
4624       }\r
4625       break;\r
4626 \r
4627     case IDM_IcsClient:\r
4628       IcsClientEvent();\r
4629       break;\r
4630 \r
4631     case IDM_EditGame:\r
4632       EditGameEvent();\r
4633       SAY("edit game");\r
4634       break;\r
4635 \r
4636     case IDM_EditPosition:\r
4637       EditPositionEvent();\r
4638       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4639       break;\r
4640 \r
4641     case IDM_Training:\r
4642       TrainingEvent();\r
4643       break;\r
4644 \r
4645     case IDM_ShowGameList:\r
4646       ShowGameListProc();\r
4647       break;\r
4648 \r
4649     case IDM_EditTags:\r
4650       EditTagsProc();\r
4651       break;\r
4652 \r
4653     case IDM_EditComment:\r
4654       if (commentUp && editComment) {\r
4655         CommentPopDown();\r
4656       } else {\r
4657         EditCommentEvent();\r
4658       }\r
4659       break;\r
4660 \r
4661     case IDM_Pause:\r
4662       PauseEvent();\r
4663       break;\r
4664 \r
4665     case IDM_Accept:\r
4666       AcceptEvent();\r
4667       break;\r
4668 \r
4669     case IDM_Decline:\r
4670       DeclineEvent();\r
4671       break;\r
4672 \r
4673     case IDM_Rematch:\r
4674       RematchEvent();\r
4675       break;\r
4676 \r
4677     case IDM_CallFlag:\r
4678       CallFlagEvent();\r
4679       break;\r
4680 \r
4681     case IDM_Draw:\r
4682       DrawEvent();\r
4683       break;\r
4684 \r
4685     case IDM_Adjourn:\r
4686       AdjournEvent();\r
4687       break;\r
4688 \r
4689     case IDM_Abort:\r
4690       AbortEvent();\r
4691       break;\r
4692 \r
4693     case IDM_Resign:\r
4694       ResignEvent();\r
4695       break;\r
4696 \r
4697     case IDM_StopObserving:\r
4698       StopObservingEvent();\r
4699       break;\r
4700 \r
4701     case IDM_StopExamining:\r
4702       StopExaminingEvent();\r
4703       break;\r
4704 \r
4705     case IDM_Upload:\r
4706       UploadGameEvent();\r
4707       break;\r
4708 \r
4709     case IDM_TypeInMove:\r
4710       PopUpMoveDialog('\000');\r
4711       break;\r
4712 \r
4713     case IDM_TypeInName:\r
4714       PopUpNameDialog('\000');\r
4715       break;\r
4716 \r
4717     case IDM_Backward:\r
4718       BackwardEvent();\r
4719       SetFocus(hwndMain);\r
4720       break;\r
4721 \r
4722     JAWS_MENU_ITEMS\r
4723 \r
4724     case IDM_Forward:\r
4725       ForwardEvent();\r
4726       SetFocus(hwndMain);\r
4727       break;\r
4728 \r
4729     case IDM_ToStart:\r
4730       ToStartEvent();\r
4731       SetFocus(hwndMain);\r
4732       break;\r
4733 \r
4734     case IDM_ToEnd:\r
4735       ToEndEvent();\r
4736       SetFocus(hwndMain);\r
4737       break;\r
4738 \r
4739     case IDM_Revert:\r
4740       RevertEvent(FALSE);\r
4741       break;\r
4742 \r
4743     case IDM_Annotate: // [HGM] vari: revert with annotation\r
4744       RevertEvent(TRUE);\r
4745       break;\r
4746 \r
4747     case IDM_TruncateGame:\r
4748       TruncateGameEvent();\r
4749       break;\r
4750 \r
4751     case IDM_MoveNow:\r
4752       MoveNowEvent();\r
4753       break;\r
4754 \r
4755     case IDM_RetractMove:\r
4756       RetractMoveEvent();\r
4757       break;\r
4758 \r
4759     case IDM_FlipView:\r
4760       flipView = !flipView;\r
4761       DrawPosition(FALSE, NULL);\r
4762       break;\r
4763 \r
4764     case IDM_FlipClock:\r
4765       flipClock = !flipClock;\r
4766       DisplayBothClocks();\r
4767       DrawPosition(FALSE, NULL);\r
4768       break;\r
4769 \r
4770     case IDM_MuteSounds:\r
4771       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4773                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4774       break;\r
4775 \r
4776     case IDM_GeneralOptions:\r
4777       GeneralOptionsPopup(hwnd);\r
4778       DrawPosition(TRUE, NULL);\r
4779       break;\r
4780 \r
4781     case IDM_BoardOptions:\r
4782       BoardOptionsPopup(hwnd);\r
4783       break;\r
4784 \r
4785     case IDM_EnginePlayOptions:\r
4786       EnginePlayOptionsPopup(hwnd);\r
4787       break;\r
4788 \r
4789     case IDM_Engine1Options:\r
4790       EngineOptionsPopup(hwnd, &first);\r
4791       break;\r
4792 \r
4793     case IDM_Engine2Options:\r
4794       EngineOptionsPopup(hwnd, &second);\r
4795       break;\r
4796 \r
4797     case IDM_OptionsUCI:\r
4798       UciOptionsPopup(hwnd);\r
4799       break;\r
4800 \r
4801     case IDM_IcsOptions:\r
4802       IcsOptionsPopup(hwnd);\r
4803       break;\r
4804 \r
4805     case IDM_Fonts:\r
4806       FontsOptionsPopup(hwnd);\r
4807       break;\r
4808 \r
4809     case IDM_Sounds:\r
4810       SoundOptionsPopup(hwnd);\r
4811       break;\r
4812 \r
4813     case IDM_CommPort:\r
4814       CommPortOptionsPopup(hwnd);\r
4815       break;\r
4816 \r
4817     case IDM_LoadOptions:\r
4818       LoadOptionsPopup(hwnd);\r
4819       break;\r
4820 \r
4821     case IDM_SaveOptions:\r
4822       SaveOptionsPopup(hwnd);\r
4823       break;\r
4824 \r
4825     case IDM_TimeControl:\r
4826       TimeControlOptionsPopup(hwnd);\r
4827       break;\r
4828 \r
4829     case IDM_SaveSettings:\r
4830       SaveSettings(settingsFileName);\r
4831       break;\r
4832 \r
4833     case IDM_SaveSettingsOnExit:\r
4834       saveSettingsOnExit = !saveSettingsOnExit;\r
4835       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4836                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4837                                          MF_CHECKED : MF_UNCHECKED));\r
4838       break;\r
4839 \r
4840     case IDM_Hint:\r
4841       HintEvent();\r
4842       break;\r
4843 \r
4844     case IDM_Book:\r
4845       BookEvent();\r
4846       break;\r
4847 \r
4848     case IDM_AboutGame:\r
4849       AboutGameEvent();\r
4850       break;\r
4851 \r
4852     case IDM_Debug:\r
4853       appData.debugMode = !appData.debugMode;\r
4854       if (appData.debugMode) {\r
4855         char dir[MSG_SIZ];\r
4856         GetCurrentDirectory(MSG_SIZ, dir);\r
4857         SetCurrentDirectory(installDir);\r
4858         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4859         SetCurrentDirectory(dir);\r
4860         setbuf(debugFP, NULL);\r
4861       } else {\r
4862         fclose(debugFP);\r
4863         debugFP = NULL;\r
4864       }\r
4865       break;\r
4866 \r
4867     case IDM_HELPCONTENTS:\r
4868       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4869           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4870           MessageBox (GetFocus(),\r
4871                     "Unable to activate help",\r
4872                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4873       }\r
4874       break;\r
4875 \r
4876     case IDM_HELPSEARCH:\r
4877         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4878             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4879         MessageBox (GetFocus(),\r
4880                     "Unable to activate help",\r
4881                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4882       }\r
4883       break;\r
4884 \r
4885     case IDM_HELPHELP:\r
4886       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4887         MessageBox (GetFocus(),\r
4888                     "Unable to activate help",\r
4889                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4890       }\r
4891       break;\r
4892 \r
4893     case IDM_ABOUT:\r
4894       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4895       DialogBox(hInst, \r
4896         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4897         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4898       FreeProcInstance(lpProc);\r
4899       break;\r
4900 \r
4901     case IDM_DirectCommand1:\r
4902       AskQuestionEvent("Direct Command",\r
4903                        "Send to chess program:", "", "1");\r
4904       break;\r
4905     case IDM_DirectCommand2:\r
4906       AskQuestionEvent("Direct Command",\r
4907                        "Send to second chess program:", "", "2");\r
4908       break;\r
4909 \r
4910     case EP_WhitePawn:\r
4911       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4912       fromX = fromY = -1;\r
4913       break;\r
4914 \r
4915     case EP_WhiteKnight:\r
4916       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4917       fromX = fromY = -1;\r
4918       break;\r
4919 \r
4920     case EP_WhiteBishop:\r
4921       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4922       fromX = fromY = -1;\r
4923       break;\r
4924 \r
4925     case EP_WhiteRook:\r
4926       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4927       fromX = fromY = -1;\r
4928       break;\r
4929 \r
4930     case EP_WhiteQueen:\r
4931       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4932       fromX = fromY = -1;\r
4933       break;\r
4934 \r
4935     case EP_WhiteFerz:\r
4936       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4937       fromX = fromY = -1;\r
4938       break;\r
4939 \r
4940     case EP_WhiteWazir:\r
4941       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4942       fromX = fromY = -1;\r
4943       break;\r
4944 \r
4945     case EP_WhiteAlfil:\r
4946       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4947       fromX = fromY = -1;\r
4948       break;\r
4949 \r
4950     case EP_WhiteCannon:\r
4951       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4952       fromX = fromY = -1;\r
4953       break;\r
4954 \r
4955     case EP_WhiteCardinal:\r
4956       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4957       fromX = fromY = -1;\r
4958       break;\r
4959 \r
4960     case EP_WhiteMarshall:\r
4961       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4962       fromX = fromY = -1;\r
4963       break;\r
4964 \r
4965     case EP_WhiteKing:\r
4966       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4967       fromX = fromY = -1;\r
4968       break;\r
4969 \r
4970     case EP_BlackPawn:\r
4971       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4972       fromX = fromY = -1;\r
4973       break;\r
4974 \r
4975     case EP_BlackKnight:\r
4976       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4977       fromX = fromY = -1;\r
4978       break;\r
4979 \r
4980     case EP_BlackBishop:\r
4981       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4982       fromX = fromY = -1;\r
4983       break;\r
4984 \r
4985     case EP_BlackRook:\r
4986       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4987       fromX = fromY = -1;\r
4988       break;\r
4989 \r
4990     case EP_BlackQueen:\r
4991       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4992       fromX = fromY = -1;\r
4993       break;\r
4994 \r
4995     case EP_BlackFerz:\r
4996       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4997       fromX = fromY = -1;\r
4998       break;\r
4999 \r
5000     case EP_BlackWazir:\r
5001       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5002       fromX = fromY = -1;\r
5003       break;\r
5004 \r
5005     case EP_BlackAlfil:\r
5006       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5007       fromX = fromY = -1;\r
5008       break;\r
5009 \r
5010     case EP_BlackCannon:\r
5011       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5012       fromX = fromY = -1;\r
5013       break;\r
5014 \r
5015     case EP_BlackCardinal:\r
5016       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5017       fromX = fromY = -1;\r
5018       break;\r
5019 \r
5020     case EP_BlackMarshall:\r
5021       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5022       fromX = fromY = -1;\r
5023       break;\r
5024 \r
5025     case EP_BlackKing:\r
5026       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5027       fromX = fromY = -1;\r
5028       break;\r
5029 \r
5030     case EP_EmptySquare:\r
5031       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5032       fromX = fromY = -1;\r
5033       break;\r
5034 \r
5035     case EP_ClearBoard:\r
5036       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5037       fromX = fromY = -1;\r
5038       break;\r
5039 \r
5040     case EP_White:\r
5041       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5042       fromX = fromY = -1;\r
5043       break;\r
5044 \r
5045     case EP_Black:\r
5046       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5047       fromX = fromY = -1;\r
5048       break;\r
5049 \r
5050     case EP_Promote:\r
5051       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5052       fromX = fromY = -1;\r
5053       break;\r
5054 \r
5055     case EP_Demote:\r
5056       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5057       fromX = fromY = -1;\r
5058       break;\r
5059 \r
5060     case DP_Pawn:\r
5061       DropMenuEvent(WhitePawn, fromX, fromY);\r
5062       fromX = fromY = -1;\r
5063       break;\r
5064 \r
5065     case DP_Knight:\r
5066       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5067       fromX = fromY = -1;\r
5068       break;\r
5069 \r
5070     case DP_Bishop:\r
5071       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5072       fromX = fromY = -1;\r
5073       break;\r
5074 \r
5075     case DP_Rook:\r
5076       DropMenuEvent(WhiteRook, fromX, fromY);\r
5077       fromX = fromY = -1;\r
5078       break;\r
5079 \r
5080     case DP_Queen:\r
5081       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5082       fromX = fromY = -1;\r
5083       break;\r
5084 \r
5085     default:\r
5086       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5087     }\r
5088     break;\r
5089 \r
5090   case WM_TIMER:\r
5091     switch (wParam) {\r
5092     case CLOCK_TIMER_ID:\r
5093       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5094       clockTimerEvent = 0;\r
5095       DecrementClocks(); /* call into back end */\r
5096       break;\r
5097     case LOAD_GAME_TIMER_ID:\r
5098       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5099       loadGameTimerEvent = 0;\r
5100       AutoPlayGameLoop(); /* call into back end */\r
5101       break;\r
5102     case ANALYSIS_TIMER_ID:\r
5103       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5104                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5105         AnalysisPeriodicEvent(0);\r
5106       } else {\r
5107         KillTimer(hwnd, analysisTimerEvent);\r
5108         analysisTimerEvent = 0;\r
5109       }\r
5110       break;\r
5111     case DELAYED_TIMER_ID:\r
5112       KillTimer(hwnd, delayedTimerEvent);\r
5113       delayedTimerEvent = 0;\r
5114       delayedTimerCallback();\r
5115       break;\r
5116     }\r
5117     break;\r
5118 \r
5119   case WM_USER_Input:\r
5120     InputEvent(hwnd, message, wParam, lParam);\r
5121     break;\r
5122 \r
5123   /* [AS] Also move "attached" child windows */\r
5124   case WM_WINDOWPOSCHANGING:\r
5125 \r
5126     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5127         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5128 \r
5129         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5130             /* Window is moving */\r
5131             RECT rcMain;\r
5132 \r
5133 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5134             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5135             rcMain.right  = wpMain.x + wpMain.width;\r
5136             rcMain.top    = wpMain.y;\r
5137             rcMain.bottom = wpMain.y + wpMain.height;\r
5138             \r
5139             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5140             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5141             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5142             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5143             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5144             wpMain.x = lpwp->x;\r
5145             wpMain.y = lpwp->y;\r
5146         }\r
5147     }\r
5148     break;\r
5149 \r
5150   /* [AS] Snapping */\r
5151   case WM_ENTERSIZEMOVE:\r
5152     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5153     if (hwnd == hwndMain) {\r
5154       doingSizing = TRUE;\r
5155       lastSizing = 0;\r
5156     }\r
5157     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5158     break;\r
5159 \r
5160   case WM_SIZING:\r
5161     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5162     if (hwnd == hwndMain) {\r
5163       lastSizing = wParam;\r
5164     }\r
5165     break;\r
5166 \r
5167   case WM_MOVING:\r
5168     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5169       return OnMoving( &sd, hwnd, wParam, lParam );\r
5170 \r
5171   case WM_EXITSIZEMOVE:\r
5172     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5173     if (hwnd == hwndMain) {\r
5174       RECT client;\r
5175       doingSizing = FALSE;\r
5176       InvalidateRect(hwnd, &boardRect, FALSE);\r
5177       GetClientRect(hwnd, &client);\r
5178       ResizeBoard(client.right, client.bottom, lastSizing);\r
5179       lastSizing = 0;\r
5180       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5181     }\r
5182     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5183     break;\r
5184 \r
5185   case WM_DESTROY: /* message: window being destroyed */\r
5186     PostQuitMessage(0);\r
5187     break;\r
5188 \r
5189   case WM_CLOSE:\r
5190     if (hwnd == hwndMain) {\r
5191       ExitEvent(0);\r
5192     }\r
5193     break;\r
5194 \r
5195   default:      /* Passes it on if unprocessed */\r
5196     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5197   }\r
5198   return 0;\r
5199 }\r
5200 \r
5201 /*---------------------------------------------------------------------------*\\r
5202  *\r
5203  * Misc utility routines\r
5204  *\r
5205 \*---------------------------------------------------------------------------*/\r
5206 \r
5207 /*\r
5208  * Decent random number generator, at least not as bad as Windows\r
5209  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5210  */\r
5211 unsigned int randstate;\r
5212 \r
5213 int\r
5214 myrandom(void)\r
5215 {\r
5216   randstate = randstate * 1664525 + 1013904223;\r
5217   return (int) randstate & 0x7fffffff;\r
5218 }\r
5219 \r
5220 void\r
5221 mysrandom(unsigned int seed)\r
5222 {\r
5223   randstate = seed;\r
5224 }\r
5225 \r
5226 \r
5227 /* \r
5228  * returns TRUE if user selects a different color, FALSE otherwise \r
5229  */\r
5230 \r
5231 BOOL\r
5232 ChangeColor(HWND hwnd, COLORREF *which)\r
5233 {\r
5234   static BOOL firstTime = TRUE;\r
5235   static DWORD customColors[16];\r
5236   CHOOSECOLOR cc;\r
5237   COLORREF newcolor;\r
5238   int i;\r
5239   ColorClass ccl;\r
5240 \r
5241   if (firstTime) {\r
5242     /* Make initial colors in use available as custom colors */\r
5243     /* Should we put the compiled-in defaults here instead? */\r
5244     i = 0;\r
5245     customColors[i++] = lightSquareColor & 0xffffff;\r
5246     customColors[i++] = darkSquareColor & 0xffffff;\r
5247     customColors[i++] = whitePieceColor & 0xffffff;\r
5248     customColors[i++] = blackPieceColor & 0xffffff;\r
5249     customColors[i++] = highlightSquareColor & 0xffffff;\r
5250     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5251 \r
5252     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5253       customColors[i++] = textAttribs[ccl].color;\r
5254     }\r
5255     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5256     firstTime = FALSE;\r
5257   }\r
5258 \r
5259   cc.lStructSize = sizeof(cc);\r
5260   cc.hwndOwner = hwnd;\r
5261   cc.hInstance = NULL;\r
5262   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5263   cc.lpCustColors = (LPDWORD) customColors;\r
5264   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5265 \r
5266   if (!ChooseColor(&cc)) return FALSE;\r
5267 \r
5268   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5269   if (newcolor == *which) return FALSE;\r
5270   *which = newcolor;\r
5271   return TRUE;\r
5272 \r
5273   /*\r
5274   InitDrawingColors();\r
5275   InvalidateRect(hwnd, &boardRect, FALSE);\r
5276   */\r
5277 }\r
5278 \r
5279 BOOLEAN\r
5280 MyLoadSound(MySound *ms)\r
5281 {\r
5282   BOOL ok = FALSE;\r
5283   struct stat st;\r
5284   FILE *f;\r
5285 \r
5286   if (ms->data) free(ms->data);\r
5287   ms->data = NULL;\r
5288 \r
5289   switch (ms->name[0]) {\r
5290   case NULLCHAR:\r
5291     /* Silence */\r
5292     ok = TRUE;\r
5293     break;\r
5294   case '$':\r
5295     /* System sound from Control Panel.  Don't preload here. */\r
5296     ok = TRUE;\r
5297     break;\r
5298   case '!':\r
5299     if (ms->name[1] == NULLCHAR) {\r
5300       /* "!" alone = silence */\r
5301       ok = TRUE;\r
5302     } else {\r
5303       /* Builtin wave resource.  Error if not found. */\r
5304       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5305       if (h == NULL) break;\r
5306       ms->data = (void *)LoadResource(hInst, h);\r
5307       if (h == NULL) break;\r
5308       ok = TRUE;\r
5309     }\r
5310     break;\r
5311   default:\r
5312     /* .wav file.  Error if not found. */\r
5313     f = fopen(ms->name, "rb");\r
5314     if (f == NULL) break;\r
5315     if (fstat(fileno(f), &st) < 0) break;\r
5316     ms->data = malloc(st.st_size);\r
5317     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5318     fclose(f);\r
5319     ok = TRUE;\r
5320     break;\r
5321   }\r
5322   if (!ok) {\r
5323     char buf[MSG_SIZ];\r
5324     sprintf(buf, "Error loading sound %s", ms->name);\r
5325     DisplayError(buf, GetLastError());\r
5326   }\r
5327   return ok;\r
5328 }\r
5329 \r
5330 BOOLEAN\r
5331 MyPlaySound(MySound *ms)\r
5332 {\r
5333   BOOLEAN ok = FALSE;\r
5334 \r
5335   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5336   switch (ms->name[0]) {\r
5337   case NULLCHAR:\r
5338         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5339     /* Silence */\r
5340     ok = TRUE;\r
5341     break;\r
5342   case '$':\r
5343     /* System sound from Control Panel (deprecated feature).\r
5344        "$" alone or an unset sound name gets default beep (still in use). */\r
5345     if (ms->name[1]) {\r
5346       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5347     }\r
5348     if (!ok) ok = MessageBeep(MB_OK);\r
5349     break; \r
5350   case '!':\r
5351     /* Builtin wave resource, or "!" alone for silence */\r
5352     if (ms->name[1]) {\r
5353       if (ms->data == NULL) return FALSE;\r
5354       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5355     } else {\r
5356       ok = TRUE;\r
5357     }\r
5358     break;\r
5359   default:\r
5360     /* .wav file.  Error if not found. */\r
5361     if (ms->data == NULL) return FALSE;\r
5362     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5363     break;\r
5364   }\r
5365   /* Don't print an error: this can happen innocently if the sound driver\r
5366      is busy; for instance, if another instance of WinBoard is playing\r
5367      a sound at about the same time. */\r
5368   return ok;\r
5369 }\r
5370 \r
5371 \r
5372 LRESULT CALLBACK\r
5373 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5374 {\r
5375   BOOL ok;\r
5376   OPENFILENAME *ofn;\r
5377   static UINT *number; /* gross that this is static */\r
5378 \r
5379   switch (message) {\r
5380   case WM_INITDIALOG: /* message: initialize dialog box */\r
5381     /* Center the dialog over the application window */\r
5382     ofn = (OPENFILENAME *) lParam;\r
5383     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5384       number = (UINT *) ofn->lCustData;\r
5385       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5386     } else {\r
5387       number = NULL;\r
5388     }\r
5389     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5390     return FALSE;  /* Allow for further processing */\r
5391 \r
5392   case WM_COMMAND:\r
5393     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5394       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5395     }\r
5396     return FALSE;  /* Allow for further processing */\r
5397   }\r
5398   return FALSE;\r
5399 }\r
5400 \r
5401 UINT APIENTRY\r
5402 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5403 {\r
5404   static UINT *number;\r
5405   OPENFILENAME *ofname;\r
5406   OFNOTIFY *ofnot;\r
5407   switch (uiMsg) {\r
5408   case WM_INITDIALOG:\r
5409     ofname = (OPENFILENAME *)lParam;\r
5410     number = (UINT *)(ofname->lCustData);\r
5411     break;\r
5412   case WM_NOTIFY:\r
5413     ofnot = (OFNOTIFY *)lParam;\r
5414     if (ofnot->hdr.code == CDN_FILEOK) {\r
5415       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5416     }\r
5417     break;\r
5418   }\r
5419   return 0;\r
5420 }\r
5421 \r
5422 \r
5423 FILE *\r
5424 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5425                char *nameFilt, char *dlgTitle, UINT *number,\r
5426                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5427 {\r
5428   OPENFILENAME openFileName;\r
5429   char buf1[MSG_SIZ];\r
5430   FILE *f;\r
5431 \r
5432   if (fileName == NULL) fileName = buf1;\r
5433   if (defName == NULL) {\r
5434     strcpy(fileName, "*.");\r
5435     strcat(fileName, defExt);\r
5436   } else {\r
5437     strcpy(fileName, defName);\r
5438   }\r
5439   if (fileTitle) strcpy(fileTitle, "");\r
5440   if (number) *number = 0;\r
5441 \r
5442   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5443   openFileName.hwndOwner         = hwnd;\r
5444   openFileName.hInstance         = (HANDLE) hInst;\r
5445   openFileName.lpstrFilter       = nameFilt;\r
5446   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5447   openFileName.nMaxCustFilter    = 0L;\r
5448   openFileName.nFilterIndex      = 1L;\r
5449   openFileName.lpstrFile         = fileName;\r
5450   openFileName.nMaxFile          = MSG_SIZ;\r
5451   openFileName.lpstrFileTitle    = fileTitle;\r
5452   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5453   openFileName.lpstrInitialDir   = NULL;\r
5454   openFileName.lpstrTitle        = dlgTitle;\r
5455   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5456     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5457     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5458     | (oldDialog ? 0 : OFN_EXPLORER);\r
5459   openFileName.nFileOffset       = 0;\r
5460   openFileName.nFileExtension    = 0;\r
5461   openFileName.lpstrDefExt       = defExt;\r
5462   openFileName.lCustData         = (LONG) number;\r
5463   openFileName.lpfnHook          = oldDialog ?\r
5464     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5465   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5466 \r
5467   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5468                         GetOpenFileName(&openFileName)) {\r
5469     /* open the file */\r
5470     f = fopen(openFileName.lpstrFile, write);\r
5471     if (f == NULL) {\r
5472       MessageBox(hwnd, "File open failed", NULL,\r
5473                  MB_OK|MB_ICONEXCLAMATION);\r
5474       return NULL;\r
5475     }\r
5476   } else {\r
5477     int err = CommDlgExtendedError();\r
5478     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5479     return FALSE;\r
5480   }\r
5481   return f;\r
5482 }\r
5483 \r
5484 \r
5485 \r
5486 VOID APIENTRY\r
5487 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5488 {\r
5489   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5490 \r
5491   /*\r
5492    * Get the first pop-up menu in the menu template. This is the\r
5493    * menu that TrackPopupMenu displays.\r
5494    */\r
5495   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5496 \r
5497   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5498 \r
5499   /*\r
5500    * TrackPopup uses screen coordinates, so convert the\r
5501    * coordinates of the mouse click to screen coordinates.\r
5502    */\r
5503   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5504 \r
5505   /* Draw and track the floating pop-up menu. */\r
5506   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5507                  pt.x, pt.y, 0, hwnd, NULL);\r
5508 \r
5509   /* Destroy the menu.*/\r
5510   DestroyMenu(hmenu);\r
5511 }\r
5512    \r
5513 typedef struct {\r
5514   HWND hDlg, hText;\r
5515   int sizeX, sizeY, newSizeX, newSizeY;\r
5516   HDWP hdwp;\r
5517 } ResizeEditPlusButtonsClosure;\r
5518 \r
5519 BOOL CALLBACK\r
5520 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5521 {\r
5522   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5523   RECT rect;\r
5524   POINT pt;\r
5525 \r
5526   if (hChild == cl->hText) return TRUE;\r
5527   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5528   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5529   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5530   ScreenToClient(cl->hDlg, &pt);\r
5531   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5532     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5533   return TRUE;\r
5534 }\r
5535 \r
5536 /* Resize a dialog that has a (rich) edit field filling most of\r
5537    the top, with a row of buttons below */\r
5538 VOID\r
5539 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5540 {\r
5541   RECT rectText;\r
5542   int newTextHeight, newTextWidth;\r
5543   ResizeEditPlusButtonsClosure cl;\r
5544   \r
5545   /*if (IsIconic(hDlg)) return;*/\r
5546   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5547   \r
5548   cl.hdwp = BeginDeferWindowPos(8);\r
5549 \r
5550   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5551   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5552   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5553   if (newTextHeight < 0) {\r
5554     newSizeY += -newTextHeight;\r
5555     newTextHeight = 0;\r
5556   }\r
5557   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5558     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5559 \r
5560   cl.hDlg = hDlg;\r
5561   cl.hText = hText;\r
5562   cl.sizeX = sizeX;\r
5563   cl.sizeY = sizeY;\r
5564   cl.newSizeX = newSizeX;\r
5565   cl.newSizeY = newSizeY;\r
5566   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5567 \r
5568   EndDeferWindowPos(cl.hdwp);\r
5569 }\r
5570 \r
5571 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5572 {\r
5573     RECT    rChild, rParent;\r
5574     int     wChild, hChild, wParent, hParent;\r
5575     int     wScreen, hScreen, xNew, yNew;\r
5576     HDC     hdc;\r
5577 \r
5578     /* Get the Height and Width of the child window */\r
5579     GetWindowRect (hwndChild, &rChild);\r
5580     wChild = rChild.right - rChild.left;\r
5581     hChild = rChild.bottom - rChild.top;\r
5582 \r
5583     /* Get the Height and Width of the parent window */\r
5584     GetWindowRect (hwndParent, &rParent);\r
5585     wParent = rParent.right - rParent.left;\r
5586     hParent = rParent.bottom - rParent.top;\r
5587 \r
5588     /* Get the display limits */\r
5589     hdc = GetDC (hwndChild);\r
5590     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5591     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5592     ReleaseDC(hwndChild, hdc);\r
5593 \r
5594     /* Calculate new X position, then adjust for screen */\r
5595     xNew = rParent.left + ((wParent - wChild) /2);\r
5596     if (xNew < 0) {\r
5597         xNew = 0;\r
5598     } else if ((xNew+wChild) > wScreen) {\r
5599         xNew = wScreen - wChild;\r
5600     }\r
5601 \r
5602     /* Calculate new Y position, then adjust for screen */\r
5603     if( mode == 0 ) {\r
5604         yNew = rParent.top  + ((hParent - hChild) /2);\r
5605     }\r
5606     else {\r
5607         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5608     }\r
5609 \r
5610     if (yNew < 0) {\r
5611         yNew = 0;\r
5612     } else if ((yNew+hChild) > hScreen) {\r
5613         yNew = hScreen - hChild;\r
5614     }\r
5615 \r
5616     /* Set it, and return */\r
5617     return SetWindowPos (hwndChild, NULL,\r
5618                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5619 }\r
5620 \r
5621 /* Center one window over another */\r
5622 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5623 {\r
5624     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5625 }\r
5626 \r
5627 /*---------------------------------------------------------------------------*\\r
5628  *\r
5629  * Startup Dialog functions\r
5630  *\r
5631 \*---------------------------------------------------------------------------*/\r
5632 void\r
5633 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5634 {\r
5635   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5636 \r
5637   while (*cd != NULL) {\r
5638     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5639     cd++;\r
5640   }\r
5641 }\r
5642 \r
5643 void\r
5644 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5645 {\r
5646   char buf1[MAX_ARG_LEN];\r
5647   int len;\r
5648 \r
5649   if (str[0] == '@') {\r
5650     FILE* f = fopen(str + 1, "r");\r
5651     if (f == NULL) {\r
5652       DisplayFatalError(str + 1, errno, 2);\r
5653       return;\r
5654     }\r
5655     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5656     fclose(f);\r
5657     buf1[len] = NULLCHAR;\r
5658     str = buf1;\r
5659   }\r
5660 \r
5661   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5662 \r
5663   for (;;) {\r
5664     char buf[MSG_SIZ];\r
5665     char *end = strchr(str, '\n');\r
5666     if (end == NULL) return;\r
5667     memcpy(buf, str, end - str);\r
5668     buf[end - str] = NULLCHAR;\r
5669     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5670     str = end + 1;\r
5671   }\r
5672 }\r
5673 \r
5674 void\r
5675 SetStartupDialogEnables(HWND hDlg)\r
5676 {\r
5677   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5678     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5679     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5680   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5681     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5682   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5683     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5684   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5685     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5686   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5687     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5688     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5689     IsDlgButtonChecked(hDlg, OPT_View));\r
5690 }\r
5691 \r
5692 char *\r
5693 QuoteForFilename(char *filename)\r
5694 {\r
5695   int dquote, space;\r
5696   dquote = strchr(filename, '"') != NULL;\r
5697   space = strchr(filename, ' ') != NULL;\r
5698   if (dquote || space) {\r
5699     if (dquote) {\r
5700       return "'";\r
5701     } else {\r
5702       return "\"";\r
5703     }\r
5704   } else {\r
5705     return "";\r
5706   }\r
5707 }\r
5708 \r
5709 VOID\r
5710 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5711 {\r
5712   char buf[MSG_SIZ];\r
5713   char *q;\r
5714 \r
5715   InitComboStringsFromOption(hwndCombo, nthnames);\r
5716   q = QuoteForFilename(nthcp);\r
5717   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5718   if (*nthdir != NULLCHAR) {\r
5719     q = QuoteForFilename(nthdir);\r
5720     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5721   }\r
5722   if (*nthcp == NULLCHAR) {\r
5723     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5724   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5725     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5726     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5727   }\r
5728 }\r
5729 \r
5730 LRESULT CALLBACK\r
5731 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5732 {\r
5733   char buf[MSG_SIZ];\r
5734   HANDLE hwndCombo;\r
5735   char *p;\r
5736 \r
5737   switch (message) {\r
5738   case WM_INITDIALOG:\r
5739     /* Center the dialog */\r
5740     CenterWindow (hDlg, GetDesktopWindow());\r
5741     /* Initialize the dialog items */\r
5742     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5743                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5744                   firstChessProgramNames);\r
5745     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5746                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5747                   secondChessProgramNames);\r
5748     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5749     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5750     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5751     if (*appData.icsHelper != NULLCHAR) {\r
5752       char *q = QuoteForFilename(appData.icsHelper);\r
5753       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5754     }\r
5755     if (*appData.icsHost == NULLCHAR) {\r
5756       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5757       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5758     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5759       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5760       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5761     }\r
5762 \r
5763     if (appData.icsActive) {\r
5764       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5765     }\r
5766     else if (appData.noChessProgram) {\r
5767       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5768     }\r
5769     else {\r
5770       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5771     }\r
5772 \r
5773     SetStartupDialogEnables(hDlg);\r
5774     return TRUE;\r
5775 \r
5776   case WM_COMMAND:\r
5777     switch (LOWORD(wParam)) {\r
5778     case IDOK:\r
5779       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5780         strcpy(buf, "/fcp=");\r
5781         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5782         p = buf;\r
5783         ParseArgs(StringGet, &p);\r
5784         strcpy(buf, "/scp=");\r
5785         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5786         p = buf;\r
5787         ParseArgs(StringGet, &p);\r
5788         appData.noChessProgram = FALSE;\r
5789         appData.icsActive = FALSE;\r
5790       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5791         strcpy(buf, "/ics /icshost=");\r
5792         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5793         p = buf;\r
5794         ParseArgs(StringGet, &p);\r
5795         if (appData.zippyPlay) {\r
5796           strcpy(buf, "/fcp=");\r
5797           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5798           p = buf;\r
5799           ParseArgs(StringGet, &p);\r
5800         }\r
5801       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5802         appData.noChessProgram = TRUE;\r
5803         appData.icsActive = FALSE;\r
5804       } else {\r
5805         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5806                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5807         return TRUE;\r
5808       }\r
5809       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5810         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5811         p = buf;\r
5812         ParseArgs(StringGet, &p);\r
5813       }\r
5814       EndDialog(hDlg, TRUE);\r
5815       return TRUE;\r
5816 \r
5817     case IDCANCEL:\r
5818       ExitEvent(0);\r
5819       return TRUE;\r
5820 \r
5821     case IDM_HELPCONTENTS:\r
5822       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5823         MessageBox (GetFocus(),\r
5824                     "Unable to activate help",\r
5825                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5826       }\r
5827       break;\r
5828 \r
5829     default:\r
5830       SetStartupDialogEnables(hDlg);\r
5831       break;\r
5832     }\r
5833     break;\r
5834   }\r
5835   return FALSE;\r
5836 }\r
5837 \r
5838 /*---------------------------------------------------------------------------*\\r
5839  *\r
5840  * About box dialog functions\r
5841  *\r
5842 \*---------------------------------------------------------------------------*/\r
5843 \r
5844 /* Process messages for "About" dialog box */\r
5845 LRESULT CALLBACK\r
5846 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5847 {\r
5848   switch (message) {\r
5849   case WM_INITDIALOG: /* message: initialize dialog box */\r
5850     /* Center the dialog over the application window */\r
5851     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5852     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5853     JAWS_COPYRIGHT\r
5854     return (TRUE);\r
5855 \r
5856   case WM_COMMAND: /* message: received a command */\r
5857     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5858         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5859       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5860       return (TRUE);\r
5861     }\r
5862     break;\r
5863   }\r
5864   return (FALSE);\r
5865 }\r
5866 \r
5867 /*---------------------------------------------------------------------------*\\r
5868  *\r
5869  * Comment Dialog functions\r
5870  *\r
5871 \*---------------------------------------------------------------------------*/\r
5872 \r
5873 LRESULT CALLBACK\r
5874 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5875 {\r
5876   static HANDLE hwndText = NULL;\r
5877   int len, newSizeX, newSizeY, flags;\r
5878   static int sizeX, sizeY;\r
5879   char *str;\r
5880   RECT rect;\r
5881   MINMAXINFO *mmi;\r
5882 \r
5883   switch (message) {\r
5884   case WM_INITDIALOG: /* message: initialize dialog box */\r
5885     /* Initialize the dialog items */\r
5886     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5887     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5888     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5889     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5890     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5891     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5892     SetWindowText(hDlg, commentTitle);\r
5893     if (editComment) {\r
5894       SetFocus(hwndText);\r
5895     } else {\r
5896       SetFocus(GetDlgItem(hDlg, IDOK));\r
5897     }\r
5898     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5899                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5900                 MAKELPARAM(FALSE, 0));\r
5901     /* Size and position the dialog */\r
5902     if (!commentDialog) {\r
5903       commentDialog = hDlg;\r
5904       flags = SWP_NOZORDER;\r
5905       GetClientRect(hDlg, &rect);\r
5906       sizeX = rect.right;\r
5907       sizeY = rect.bottom;\r
5908       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5909           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5910         WINDOWPLACEMENT wp;\r
5911         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5912         wp.length = sizeof(WINDOWPLACEMENT);\r
5913         wp.flags = 0;\r
5914         wp.showCmd = SW_SHOW;\r
5915         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5916         wp.rcNormalPosition.left = wpComment.x;\r
5917         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5918         wp.rcNormalPosition.top = wpComment.y;\r
5919         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5920         SetWindowPlacement(hDlg, &wp);\r
5921 \r
5922         GetClientRect(hDlg, &rect);\r
5923         newSizeX = rect.right;\r
5924         newSizeY = rect.bottom;\r
5925         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5926                               newSizeX, newSizeY);\r
5927         sizeX = newSizeX;\r
5928         sizeY = newSizeY;\r
5929       }\r
5930     }\r
5931     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
5932     return FALSE;\r
5933 \r
5934   case WM_COMMAND: /* message: received a command */\r
5935     switch (LOWORD(wParam)) {\r
5936     case IDOK:\r
5937       if (editComment) {\r
5938         char *p, *q;\r
5939         /* Read changed options from the dialog box */\r
5940         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5941         len = GetWindowTextLength(hwndText);\r
5942         str = (char *) malloc(len + 1);\r
5943         GetWindowText(hwndText, str, len + 1);\r
5944         p = q = str;\r
5945         while (*q) {\r
5946           if (*q == '\r')\r
5947             q++;\r
5948           else\r
5949             *p++ = *q++;\r
5950         }\r
5951         *p = NULLCHAR;\r
5952         ReplaceComment(commentIndex, str);\r
5953         free(str);\r
5954       }\r
5955       CommentPopDown();\r
5956       return TRUE;\r
5957 \r
5958     case IDCANCEL:\r
5959     case OPT_CancelComment:\r
5960       CommentPopDown();\r
5961       return TRUE;\r
5962 \r
5963     case OPT_ClearComment:\r
5964       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5965       break;\r
5966 \r
5967     case OPT_EditComment:\r
5968       EditCommentEvent();\r
5969       return TRUE;\r
5970 \r
5971     default:\r
5972       break;\r
5973     }\r
5974     break;\r
5975 \r
5976   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
5977         if( wParam == OPT_CommentText ) {\r
5978             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
5979 \r
5980             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
5981                 POINTL pt;\r
5982                 LRESULT index;\r
5983 \r
5984                 pt.x = LOWORD( lpMF->lParam );\r
5985                 pt.y = HIWORD( lpMF->lParam );\r
5986 \r
5987                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
5988 \r
5989                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
5990                 len = GetWindowTextLength(hwndText);\r
5991                 str = (char *) malloc(len + 1);\r
5992                 GetWindowText(hwndText, str, len + 1);\r
5993                 ReplaceComment(commentIndex, str);\r
5994                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
5995                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
5996                 free(str);\r
5997 \r
5998                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
5999                 lpMF->msg = WM_USER;\r
6000 \r
6001                 return TRUE;\r
6002             }\r
6003         }\r
6004         break;\r
6005 \r
6006   case WM_SIZE:\r
6007     newSizeX = LOWORD(lParam);\r
6008     newSizeY = HIWORD(lParam);\r
6009     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6010     sizeX = newSizeX;\r
6011     sizeY = newSizeY;\r
6012     break;\r
6013 \r
6014   case WM_GETMINMAXINFO:\r
6015     /* Prevent resizing window too small */\r
6016     mmi = (MINMAXINFO *) lParam;\r
6017     mmi->ptMinTrackSize.x = 100;\r
6018     mmi->ptMinTrackSize.y = 100;\r
6019     break;\r
6020   }\r
6021   return FALSE;\r
6022 }\r
6023 \r
6024 VOID\r
6025 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6026 {\r
6027   FARPROC lpProc;\r
6028   char *p, *q;\r
6029 \r
6030   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6031 \r
6032   if (str == NULL) str = "";\r
6033   p = (char *) malloc(2 * strlen(str) + 2);\r
6034   q = p;\r
6035   while (*str) {\r
6036     if (*str == '\n') *q++ = '\r';\r
6037     *q++ = *str++;\r
6038   }\r
6039   *q = NULLCHAR;\r
6040   if (commentText != NULL) free(commentText);\r
6041 \r
6042   commentIndex = index;\r
6043   commentTitle = title;\r
6044   commentText = p;\r
6045   editComment = edit;\r
6046 \r
6047   if (commentDialog) {\r
6048     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6049     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6050   } else {\r
6051     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6052     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6053                  hwndMain, (DLGPROC)lpProc);\r
6054     FreeProcInstance(lpProc);\r
6055   }\r
6056   commentUp = TRUE;\r
6057 }\r
6058 \r
6059 \r
6060 /*---------------------------------------------------------------------------*\\r
6061  *\r
6062  * Type-in move dialog functions\r
6063  * \r
6064 \*---------------------------------------------------------------------------*/\r
6065 \r
6066 LRESULT CALLBACK\r
6067 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6068 {\r
6069   char move[MSG_SIZ];\r
6070   HWND hInput;\r
6071   ChessMove moveType;\r
6072   int fromX, fromY, toX, toY;\r
6073   char promoChar;\r
6074 \r
6075   switch (message) {\r
6076   case WM_INITDIALOG:\r
6077     move[0] = (char) lParam;\r
6078     move[1] = NULLCHAR;\r
6079     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6080     hInput = GetDlgItem(hDlg, OPT_Move);\r
6081     SetWindowText(hInput, move);\r
6082     SetFocus(hInput);\r
6083     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6084     return FALSE;\r
6085 \r
6086   case WM_COMMAND:\r
6087     switch (LOWORD(wParam)) {\r
6088     case IDOK:\r
6089       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6090       { int n; Board board;\r
6091         // [HGM] FENedit\r
6092         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6093                 EditPositionPasteFEN(move);\r
6094                 EndDialog(hDlg, TRUE);\r
6095                 return TRUE;\r
6096         }\r
6097         // [HGM] movenum: allow move number to be typed in any mode\r
6098         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6099           ToNrEvent(2*n-1);\r
6100           EndDialog(hDlg, TRUE);\r
6101           return TRUE;\r
6102         }\r
6103       }\r
6104       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6105         gameMode != Training) {\r
6106         DisplayMoveError("Displayed move is not current");\r
6107       } else {\r
6108 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6109         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6110           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6111         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6112         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6113           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6114           if (gameMode != Training)\r
6115               forwardMostMove = currentMove;\r
6116           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6117         } else {\r
6118           DisplayMoveError("Could not parse move");\r
6119         }\r
6120       }\r
6121       EndDialog(hDlg, TRUE);\r
6122       return TRUE;\r
6123     case IDCANCEL:\r
6124       EndDialog(hDlg, FALSE);\r
6125       return TRUE;\r
6126     default:\r
6127       break;\r
6128     }\r
6129     break;\r
6130   }\r
6131   return FALSE;\r
6132 }\r
6133 \r
6134 VOID\r
6135 PopUpMoveDialog(char firstchar)\r
6136 {\r
6137     FARPROC lpProc;\r
6138     \r
6139     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6140         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6141         gameMode == AnalyzeMode || gameMode == EditGame || \r
6142         gameMode == EditPosition || gameMode == IcsExamining ||\r
6143         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6144         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6145                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6146                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6147         gameMode == Training) {\r
6148       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6149       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6150         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6151       FreeProcInstance(lpProc);\r
6152     }\r
6153 }\r
6154 \r
6155 /*---------------------------------------------------------------------------*\\r
6156  *\r
6157  * Type-in name dialog functions\r
6158  * \r
6159 \*---------------------------------------------------------------------------*/\r
6160 \r
6161 LRESULT CALLBACK\r
6162 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6163 {\r
6164   char move[MSG_SIZ];\r
6165   HWND hInput;\r
6166 \r
6167   switch (message) {\r
6168   case WM_INITDIALOG:\r
6169     move[0] = (char) lParam;\r
6170     move[1] = NULLCHAR;\r
6171     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6172     hInput = GetDlgItem(hDlg, OPT_Name);\r
6173     SetWindowText(hInput, move);\r
6174     SetFocus(hInput);\r
6175     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6176     return FALSE;\r
6177 \r
6178   case WM_COMMAND:\r
6179     switch (LOWORD(wParam)) {\r
6180     case IDOK:\r
6181       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6182       appData.userName = strdup(move);\r
6183       SetUserLogo();\r
6184       SetGameInfo();\r
6185       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6186         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6187         DisplayTitle(move);\r
6188       }\r
6189 \r
6190 \r
6191       EndDialog(hDlg, TRUE);\r
6192       return TRUE;\r
6193     case IDCANCEL:\r
6194       EndDialog(hDlg, FALSE);\r
6195       return TRUE;\r
6196     default:\r
6197       break;\r
6198     }\r
6199     break;\r
6200   }\r
6201   return FALSE;\r
6202 }\r
6203 \r
6204 VOID\r
6205 PopUpNameDialog(char firstchar)\r
6206 {\r
6207     FARPROC lpProc;\r
6208     \r
6209       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6210       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6211         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6212       FreeProcInstance(lpProc);\r
6213 }\r
6214 \r
6215 /*---------------------------------------------------------------------------*\\r
6216  *\r
6217  *  Error dialogs\r
6218  * \r
6219 \*---------------------------------------------------------------------------*/\r
6220 \r
6221 /* Nonmodal error box */\r
6222 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6223                              WPARAM wParam, LPARAM lParam);\r
6224 \r
6225 VOID\r
6226 ErrorPopUp(char *title, char *content)\r
6227 {\r
6228   FARPROC lpProc;\r
6229   char *p, *q;\r
6230   BOOLEAN modal = hwndMain == NULL;\r
6231 \r
6232   p = content;\r
6233   q = errorMessage;\r
6234   while (*p) {\r
6235     if (*p == '\n') {\r
6236       if (modal) {\r
6237         *q++ = ' ';\r
6238         p++;\r
6239       } else {\r
6240         *q++ = '\r';\r
6241         *q++ = *p++;\r
6242       }\r
6243     } else {\r
6244       *q++ = *p++;\r
6245     }\r
6246   }\r
6247   *q = NULLCHAR;\r
6248   strncpy(errorTitle, title, sizeof(errorTitle));\r
6249   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6250   \r
6251   if (modal) {\r
6252     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6253   } else {\r
6254     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6255     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6256                  hwndMain, (DLGPROC)lpProc);\r
6257     FreeProcInstance(lpProc);\r
6258   }\r
6259 }\r
6260 \r
6261 VOID\r
6262 ErrorPopDown()\r
6263 {\r
6264   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6265   if (errorDialog == NULL) return;\r
6266   DestroyWindow(errorDialog);\r
6267   errorDialog = NULL;\r
6268   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6269 }\r
6270 \r
6271 LRESULT CALLBACK\r
6272 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6273 {\r
6274   HANDLE hwndText;\r
6275   RECT rChild;\r
6276 \r
6277   switch (message) {\r
6278   case WM_INITDIALOG:\r
6279     GetWindowRect(hDlg, &rChild);\r
6280 \r
6281     /*\r
6282     SetWindowPos(hDlg, NULL, rChild.left,\r
6283       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6284       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6285     */\r
6286 \r
6287     /* \r
6288         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6289         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6290         and it doesn't work when you resize the dialog.\r
6291         For now, just give it a default position.\r
6292     */\r
6293     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6294 \r
6295     errorDialog = hDlg;\r
6296     SetWindowText(hDlg, errorTitle);\r
6297     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6298     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6299     return FALSE;\r
6300 \r
6301   case WM_COMMAND:\r
6302     switch (LOWORD(wParam)) {\r
6303     case IDOK:\r
6304     case IDCANCEL:\r
6305       if (errorDialog == hDlg) errorDialog = NULL;\r
6306       DestroyWindow(hDlg);\r
6307       return TRUE;\r
6308 \r
6309     default:\r
6310       break;\r
6311     }\r
6312     break;\r
6313   }\r
6314   return FALSE;\r
6315 }\r
6316 \r
6317 #ifdef GOTHIC\r
6318 HWND gothicDialog = NULL;\r
6319 \r
6320 LRESULT CALLBACK\r
6321 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6322 {\r
6323   HANDLE hwndText;\r
6324   RECT rChild;\r
6325   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6326 \r
6327   switch (message) {\r
6328   case WM_INITDIALOG:\r
6329     GetWindowRect(hDlg, &rChild);\r
6330 \r
6331     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6332                                                              SWP_NOZORDER);\r
6333 \r
6334     /* \r
6335         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6336         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6337         and it doesn't work when you resize the dialog.\r
6338         For now, just give it a default position.\r
6339     */\r
6340     gothicDialog = hDlg;\r
6341     SetWindowText(hDlg, errorTitle);\r
6342     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6343     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6344     return FALSE;\r
6345 \r
6346   case WM_COMMAND:\r
6347     switch (LOWORD(wParam)) {\r
6348     case IDOK:\r
6349     case IDCANCEL:\r
6350       if (errorDialog == hDlg) errorDialog = NULL;\r
6351       DestroyWindow(hDlg);\r
6352       return TRUE;\r
6353 \r
6354     default:\r
6355       break;\r
6356     }\r
6357     break;\r
6358   }\r
6359   return FALSE;\r
6360 }\r
6361 \r
6362 VOID\r
6363 GothicPopUp(char *title, VariantClass variant)\r
6364 {\r
6365   FARPROC lpProc;\r
6366   static char *lastTitle;\r
6367 \r
6368   strncpy(errorTitle, title, sizeof(errorTitle));\r
6369   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6370 \r
6371   if(lastTitle != title && gothicDialog != NULL) {\r
6372     DestroyWindow(gothicDialog);\r
6373     gothicDialog = NULL;\r
6374   }\r
6375   if(variant != VariantNormal && gothicDialog == NULL) {\r
6376     title = lastTitle;\r
6377     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6378     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6379                  hwndMain, (DLGPROC)lpProc);\r
6380     FreeProcInstance(lpProc);\r
6381   }\r
6382 }\r
6383 #endif\r
6384 \r
6385 /*---------------------------------------------------------------------------*\\r
6386  *\r
6387  *  Ics Interaction console functions\r
6388  *\r
6389 \*---------------------------------------------------------------------------*/\r
6390 \r
6391 #define HISTORY_SIZE 64\r
6392 static char *history[HISTORY_SIZE];\r
6393 int histIn = 0, histP = 0;\r
6394 \r
6395 VOID\r
6396 SaveInHistory(char *cmd)\r
6397 {\r
6398   if (history[histIn] != NULL) {\r
6399     free(history[histIn]);\r
6400     history[histIn] = NULL;\r
6401   }\r
6402   if (*cmd == NULLCHAR) return;\r
6403   history[histIn] = StrSave(cmd);\r
6404   histIn = (histIn + 1) % HISTORY_SIZE;\r
6405   if (history[histIn] != NULL) {\r
6406     free(history[histIn]);\r
6407     history[histIn] = NULL;\r
6408   }\r
6409   histP = histIn;\r
6410 }\r
6411 \r
6412 char *\r
6413 PrevInHistory(char *cmd)\r
6414 {\r
6415   int newhp;\r
6416   if (histP == histIn) {\r
6417     if (history[histIn] != NULL) free(history[histIn]);\r
6418     history[histIn] = StrSave(cmd);\r
6419   }\r
6420   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6421   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6422   histP = newhp;\r
6423   return history[histP];\r
6424 }\r
6425 \r
6426 char *\r
6427 NextInHistory()\r
6428 {\r
6429   if (histP == histIn) return NULL;\r
6430   histP = (histP + 1) % HISTORY_SIZE;\r
6431   return history[histP];   \r
6432 }\r
6433 \r
6434 HMENU\r
6435 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6436 {\r
6437   HMENU hmenu, h;\r
6438   int i = 0;\r
6439   hmenu = LoadMenu(hInst, "TextMenu");\r
6440   h = GetSubMenu(hmenu, 0);\r
6441   while (e->item) {\r
6442     if (strcmp(e->item, "-") == 0) {\r
6443       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6444     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6445       int flags = MF_STRING, j = 0;\r
6446       if (e->item[0] == '|') {\r
6447         flags |= MF_MENUBARBREAK;\r
6448         j++;\r
6449       }\r
6450       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6451       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6452     }\r
6453     e++;\r
6454     i++;\r
6455   } \r
6456   return hmenu;\r
6457 }\r
6458 \r
6459 WNDPROC consoleTextWindowProc;\r
6460 \r
6461 void\r
6462 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6463 {\r
6464   char buf[MSG_SIZ], name[MSG_SIZ];\r
6465   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6466   CHARRANGE sel;\r
6467 \r
6468   if (!getname) {\r
6469     SetWindowText(hInput, command);\r
6470     if (immediate) {\r
6471       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6472     } else {\r
6473       sel.cpMin = 999999;\r
6474       sel.cpMax = 999999;\r
6475       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6476       SetFocus(hInput);\r
6477     }\r
6478     return;\r
6479   }    \r
6480   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6481   if (sel.cpMin == sel.cpMax) {\r
6482     /* Expand to surrounding word */\r
6483     TEXTRANGE tr;\r
6484     do {\r
6485       tr.chrg.cpMax = sel.cpMin;\r
6486       tr.chrg.cpMin = --sel.cpMin;\r
6487       if (sel.cpMin < 0) break;\r
6488       tr.lpstrText = name;\r
6489       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6490     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6491     sel.cpMin++;\r
6492 \r
6493     do {\r
6494       tr.chrg.cpMin = sel.cpMax;\r
6495       tr.chrg.cpMax = ++sel.cpMax;\r
6496       tr.lpstrText = name;\r
6497       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6498     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6499     sel.cpMax--;\r
6500 \r
6501     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6502       MessageBeep(MB_ICONEXCLAMATION);\r
6503       return;\r
6504     }\r
6505     tr.chrg = sel;\r
6506     tr.lpstrText = name;\r
6507     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6508   } else {\r
6509     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6510       MessageBeep(MB_ICONEXCLAMATION);\r
6511       return;\r
6512     }\r
6513     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6514   }\r
6515   if (immediate) {\r
6516     sprintf(buf, "%s %s", command, name);\r
6517     SetWindowText(hInput, buf);\r
6518     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6519   } else {\r
6520     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6521     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6522     SetWindowText(hInput, buf);\r
6523     sel.cpMin = 999999;\r
6524     sel.cpMax = 999999;\r
6525     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6526     SetFocus(hInput);\r
6527   }\r
6528 }\r
6529 \r
6530 LRESULT CALLBACK \r
6531 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6532 {\r
6533   HWND hInput;\r
6534   CHARRANGE sel;\r
6535 \r
6536   switch (message) {\r
6537   case WM_KEYDOWN:\r
6538     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6539     switch (wParam) {\r
6540     case VK_PRIOR:\r
6541       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6542       return 0;\r
6543     case VK_NEXT:\r
6544       sel.cpMin = 999999;\r
6545       sel.cpMax = 999999;\r
6546       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6547       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6548       return 0;\r
6549     }\r
6550     break;\r
6551   case WM_CHAR:\r
6552    if(wParam != '\022') {\r
6553     if (wParam == '\t') {\r
6554       if (GetKeyState(VK_SHIFT) < 0) {\r
6555         /* shifted */\r
6556         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6557         if (buttonDesc[0].hwnd) {\r
6558           SetFocus(buttonDesc[0].hwnd);\r
6559         } else {\r
6560           SetFocus(hwndMain);\r
6561         }\r
6562       } else {\r
6563         /* unshifted */\r
6564         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6565       }\r
6566     } else {\r
6567       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6568       JAWS_DELETE( SetFocus(hInput); )\r
6569       SendMessage(hInput, message, wParam, lParam);\r
6570     }\r
6571     return 0;\r
6572    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6573   case WM_RBUTTONDOWN:\r
6574     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6575       /* Move selection here if it was empty */\r
6576       POINT pt;\r
6577       pt.x = LOWORD(lParam);\r
6578       pt.y = HIWORD(lParam);\r
6579       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6580       if (sel.cpMin == sel.cpMax) {\r
6581         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6582         sel.cpMax = sel.cpMin;\r
6583         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6584       }\r
6585       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6586 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6587       POINT pt;\r
6588       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6589       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6590       if (sel.cpMin == sel.cpMax) {\r
6591         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6592         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6593       }\r
6594       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6595         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6596       }\r
6597       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6598       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6599       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6600       MenuPopup(hwnd, pt, hmenu, -1);\r
6601 }\r
6602     }\r
6603     return 0;\r
6604   case WM_RBUTTONUP:\r
6605     if (GetKeyState(VK_SHIFT) & ~1) {\r
6606       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6607         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6608     }\r
6609     return 0;\r
6610   case WM_PASTE:\r
6611     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6612     SetFocus(hInput);\r
6613     return SendMessage(hInput, message, wParam, lParam);\r
6614   case WM_MBUTTONDOWN:\r
6615     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6616   case WM_COMMAND:\r
6617     switch (LOWORD(wParam)) {\r
6618     case IDM_QuickPaste:\r
6619       {\r
6620         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6621         if (sel.cpMin == sel.cpMax) {\r
6622           MessageBeep(MB_ICONEXCLAMATION);\r
6623           return 0;\r
6624         }\r
6625         SendMessage(hwnd, WM_COPY, 0, 0);\r
6626         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6627         SendMessage(hInput, WM_PASTE, 0, 0);\r
6628         SetFocus(hInput);\r
6629         return 0;\r
6630       }\r
6631     case IDM_Cut:\r
6632       SendMessage(hwnd, WM_CUT, 0, 0);\r
6633       return 0;\r
6634     case IDM_Paste:\r
6635       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6636       return 0;\r
6637     case IDM_Copy:\r
6638       SendMessage(hwnd, WM_COPY, 0, 0);\r
6639       return 0;\r
6640     default:\r
6641       {\r
6642         int i = LOWORD(wParam) - IDM_CommandX;\r
6643         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6644             icsTextMenuEntry[i].command != NULL) {\r
6645           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6646                    icsTextMenuEntry[i].getname,\r
6647                    icsTextMenuEntry[i].immediate);\r
6648           return 0;\r
6649         }\r
6650       }\r
6651       break;\r
6652     }\r
6653     break;\r
6654   }\r
6655   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6656 }\r
6657 \r
6658 WNDPROC consoleInputWindowProc;\r
6659 \r
6660 LRESULT CALLBACK\r
6661 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6662 {\r
6663   char buf[MSG_SIZ];\r
6664   char *p;\r
6665   static BOOL sendNextChar = FALSE;\r
6666   static BOOL quoteNextChar = FALSE;\r
6667   InputSource *is = consoleInputSource;\r
6668   CHARFORMAT cf;\r
6669   CHARRANGE sel;\r
6670 \r
6671   switch (message) {\r
6672   case WM_CHAR:\r
6673     if (!appData.localLineEditing || sendNextChar) {\r
6674       is->buf[0] = (CHAR) wParam;\r
6675       is->count = 1;\r
6676       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6677       sendNextChar = FALSE;\r
6678       return 0;\r
6679     }\r
6680     if (quoteNextChar) {\r
6681       buf[0] = (char) wParam;\r
6682       buf[1] = NULLCHAR;\r
6683       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6684       quoteNextChar = FALSE;\r
6685       return 0;\r
6686     }\r
6687     switch (wParam) {\r
6688     case '\r':   /* Enter key */\r
6689       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6690       if (consoleEcho) SaveInHistory(is->buf);\r
6691       is->buf[is->count++] = '\n';\r
6692       is->buf[is->count] = NULLCHAR;\r
6693       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6694       if (consoleEcho) {\r
6695         ConsoleOutput(is->buf, is->count, TRUE);\r
6696       } else if (appData.localLineEditing) {\r
6697         ConsoleOutput("\n", 1, TRUE);\r
6698       }\r
6699       /* fall thru */\r
6700     case '\033': /* Escape key */\r
6701       SetWindowText(hwnd, "");\r
6702       cf.cbSize = sizeof(CHARFORMAT);\r
6703       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6704       if (consoleEcho) {\r
6705         cf.crTextColor = textAttribs[ColorNormal].color;\r
6706       } else {\r
6707         cf.crTextColor = COLOR_ECHOOFF;\r
6708       }\r
6709       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6710       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6711       return 0;\r
6712     case '\t':   /* Tab key */\r
6713       if (GetKeyState(VK_SHIFT) < 0) {\r
6714         /* shifted */\r
6715         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6716       } else {\r
6717         /* unshifted */\r
6718         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6719         if (buttonDesc[0].hwnd) {\r
6720           SetFocus(buttonDesc[0].hwnd);\r
6721         } else {\r
6722           SetFocus(hwndMain);\r
6723         }\r
6724       }\r
6725       return 0;\r
6726     case '\023': /* Ctrl+S */\r
6727       sendNextChar = TRUE;\r
6728       return 0;\r
6729     case '\021': /* Ctrl+Q */\r
6730       quoteNextChar = TRUE;\r
6731       return 0;\r
6732     JAWS_REPLAY\r
6733     default:\r
6734       break;\r
6735     }\r
6736     break;\r
6737   case WM_KEYDOWN:\r
6738     switch (wParam) {\r
6739     case VK_UP:\r
6740       GetWindowText(hwnd, buf, MSG_SIZ);\r
6741       p = PrevInHistory(buf);\r
6742       if (p != NULL) {\r
6743         SetWindowText(hwnd, p);\r
6744         sel.cpMin = 999999;\r
6745         sel.cpMax = 999999;\r
6746         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6747         return 0;\r
6748       }\r
6749       break;\r
6750     case VK_DOWN:\r
6751       p = NextInHistory();\r
6752       if (p != NULL) {\r
6753         SetWindowText(hwnd, p);\r
6754         sel.cpMin = 999999;\r
6755         sel.cpMax = 999999;\r
6756         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6757         return 0;\r
6758       }\r
6759       break;\r
6760     case VK_HOME:\r
6761     case VK_END:\r
6762       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6763       /* fall thru */\r
6764     case VK_PRIOR:\r
6765     case VK_NEXT:\r
6766       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6767       return 0;\r
6768     }\r
6769     break;\r
6770   case WM_MBUTTONDOWN:\r
6771     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6772       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6773     break;\r
6774   case WM_RBUTTONUP:\r
6775     if (GetKeyState(VK_SHIFT) & ~1) {\r
6776       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6777         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6778     } else {\r
6779       POINT pt;\r
6780       HMENU hmenu;\r
6781       hmenu = LoadMenu(hInst, "InputMenu");\r
6782       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6783       if (sel.cpMin == sel.cpMax) {\r
6784         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6785         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6786       }\r
6787       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6788         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6789       }\r
6790       pt.x = LOWORD(lParam);\r
6791       pt.y = HIWORD(lParam);\r
6792       MenuPopup(hwnd, pt, hmenu, -1);\r
6793     }\r
6794     return 0;\r
6795   case WM_COMMAND:\r
6796     switch (LOWORD(wParam)) { \r
6797     case IDM_Undo:\r
6798       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6799       return 0;\r
6800     case IDM_SelectAll:\r
6801       sel.cpMin = 0;\r
6802       sel.cpMax = -1; /*999999?*/\r
6803       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6804       return 0;\r
6805     case IDM_Cut:\r
6806       SendMessage(hwnd, WM_CUT, 0, 0);\r
6807       return 0;\r
6808     case IDM_Paste:\r
6809       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6810       return 0;\r
6811     case IDM_Copy:\r
6812       SendMessage(hwnd, WM_COPY, 0, 0);\r
6813       return 0;\r
6814     }\r
6815     break;\r
6816   }\r
6817   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6818 }\r
6819 \r
6820 #define CO_MAX  100000\r
6821 #define CO_TRIM   1000\r
6822 \r
6823 LRESULT CALLBACK\r
6824 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6825 {\r
6826   static SnapData sd;\r
6827   HWND hText, hInput;\r
6828   RECT rect;\r
6829   static int sizeX, sizeY;\r
6830   int newSizeX, newSizeY;\r
6831   MINMAXINFO *mmi;\r
6832   WORD wMask;\r
6833 \r
6834   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6835   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6836 \r
6837   switch (message) {\r
6838   case WM_NOTIFY:\r
6839     if (((NMHDR*)lParam)->code == EN_LINK)\r
6840     {\r
6841       ENLINK *pLink = (ENLINK*)lParam;\r
6842       if (pLink->msg == WM_LBUTTONUP)\r
6843       {\r
6844         TEXTRANGE tr;\r
6845 \r
6846         tr.chrg = pLink->chrg;\r
6847         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
6848         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
6849         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
6850         free(tr.lpstrText);\r
6851       }\r
6852     }\r
6853     break;\r
6854   case WM_INITDIALOG: /* message: initialize dialog box */\r
6855     hwndConsole = hDlg;\r
6856     SetFocus(hInput);\r
6857     consoleTextWindowProc = (WNDPROC)\r
6858       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6859     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6860     consoleInputWindowProc = (WNDPROC)\r
6861       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6862     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6863     Colorize(ColorNormal, TRUE);\r
6864     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6865     ChangedConsoleFont();\r
6866     GetClientRect(hDlg, &rect);\r
6867     sizeX = rect.right;\r
6868     sizeY = rect.bottom;\r
6869     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
6870         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
6871       WINDOWPLACEMENT wp;\r
6872       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6873       wp.length = sizeof(WINDOWPLACEMENT);\r
6874       wp.flags = 0;\r
6875       wp.showCmd = SW_SHOW;\r
6876       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6877       wp.rcNormalPosition.left = wpConsole.x;\r
6878       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6879       wp.rcNormalPosition.top = wpConsole.y;\r
6880       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6881       SetWindowPlacement(hDlg, &wp);\r
6882     }\r
6883 \r
6884    // [HGM] Chessknight's change 2004-07-13\r
6885    else { /* Determine Defaults */\r
6886        WINDOWPLACEMENT wp;\r
6887        wpConsole.x = wpMain.width + 1;\r
6888        wpConsole.y = wpMain.y;\r
6889        wpConsole.width = screenWidth -  wpMain.width;\r
6890        wpConsole.height = wpMain.height;\r
6891        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
6892        wp.length = sizeof(WINDOWPLACEMENT);\r
6893        wp.flags = 0;\r
6894        wp.showCmd = SW_SHOW;\r
6895        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6896        wp.rcNormalPosition.left = wpConsole.x;\r
6897        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
6898        wp.rcNormalPosition.top = wpConsole.y;\r
6899        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
6900        SetWindowPlacement(hDlg, &wp);\r
6901     }\r
6902 \r
6903    // Allow hText to highlight URLs and send notifications on them\r
6904    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
6905    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
6906    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
6907    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
6908 \r
6909     return FALSE;\r
6910 \r
6911   case WM_SETFOCUS:\r
6912     SetFocus(hInput);\r
6913     return 0;\r
6914 \r
6915   case WM_CLOSE:\r
6916     ExitEvent(0);\r
6917     /* not reached */\r
6918     break;\r
6919 \r
6920   case WM_SIZE:\r
6921     if (IsIconic(hDlg)) break;\r
6922     newSizeX = LOWORD(lParam);\r
6923     newSizeY = HIWORD(lParam);\r
6924     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6925       RECT rectText, rectInput;\r
6926       POINT pt;\r
6927       int newTextHeight, newTextWidth;\r
6928       GetWindowRect(hText, &rectText);\r
6929       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6930       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6931       if (newTextHeight < 0) {\r
6932         newSizeY += -newTextHeight;\r
6933         newTextHeight = 0;\r
6934       }\r
6935       SetWindowPos(hText, NULL, 0, 0,\r
6936         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6937       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6938       pt.x = rectInput.left;\r
6939       pt.y = rectInput.top + newSizeY - sizeY;\r
6940       ScreenToClient(hDlg, &pt);\r
6941       SetWindowPos(hInput, NULL, \r
6942         pt.x, pt.y, /* needs client coords */   \r
6943         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6944         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6945     }\r
6946     sizeX = newSizeX;\r
6947     sizeY = newSizeY;\r
6948     break;\r
6949 \r
6950   case WM_GETMINMAXINFO:\r
6951     /* Prevent resizing window too small */\r
6952     mmi = (MINMAXINFO *) lParam;\r
6953     mmi->ptMinTrackSize.x = 100;\r
6954     mmi->ptMinTrackSize.y = 100;\r
6955     break;\r
6956 \r
6957   /* [AS] Snapping */\r
6958   case WM_ENTERSIZEMOVE:\r
6959     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
6960 \r
6961   case WM_SIZING:\r
6962     return OnSizing( &sd, hDlg, wParam, lParam );\r
6963 \r
6964   case WM_MOVING:\r
6965     return OnMoving( &sd, hDlg, wParam, lParam );\r
6966 \r
6967   case WM_EXITSIZEMOVE:\r
6968         UpdateICSWidth(hText);\r
6969     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
6970   }\r
6971 \r
6972   return DefWindowProc(hDlg, message, wParam, lParam);\r
6973 }\r
6974 \r
6975 \r
6976 VOID\r
6977 ConsoleCreate()\r
6978 {\r
6979   HWND hCons;\r
6980   if (hwndConsole) return;\r
6981   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6982   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6983 }\r
6984 \r
6985 \r
6986 VOID\r
6987 ConsoleOutput(char* data, int length, int forceVisible)\r
6988 {\r
6989   HWND hText;\r
6990   int trim, exlen;\r
6991   char *p, *q;\r
6992   char buf[CO_MAX+1];\r
6993   POINT pEnd;\r
6994   RECT rect;\r
6995   static int delayLF = 0;\r
6996   CHARRANGE savesel, sel;\r
6997 \r
6998   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6999   p = data;\r
7000   q = buf;\r
7001   if (delayLF) {\r
7002     *q++ = '\r';\r
7003     *q++ = '\n';\r
7004     delayLF = 0;\r
7005   }\r
7006   while (length--) {\r
7007     if (*p == '\n') {\r
7008       if (*++p) {\r
7009         *q++ = '\r';\r
7010         *q++ = '\n';\r
7011       } else {\r
7012         delayLF = 1;\r
7013       }\r
7014     } else if (*p == '\007') {\r
7015        MyPlaySound(&sounds[(int)SoundBell]);\r
7016        p++;\r
7017     } else {\r
7018       *q++ = *p++;\r
7019     }\r
7020   }\r
7021   *q = NULLCHAR;\r
7022   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7023   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7024   /* Save current selection */\r
7025   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7026   exlen = GetWindowTextLength(hText);\r
7027   /* Find out whether current end of text is visible */\r
7028   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7029   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7030   /* Trim existing text if it's too long */\r
7031   if (exlen + (q - buf) > CO_MAX) {\r
7032     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7033     sel.cpMin = 0;\r
7034     sel.cpMax = trim;\r
7035     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7036     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7037     exlen -= trim;\r
7038     savesel.cpMin -= trim;\r
7039     savesel.cpMax -= trim;\r
7040     if (exlen < 0) exlen = 0;\r
7041     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7042     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7043   }\r
7044   /* Append the new text */\r
7045   sel.cpMin = exlen;\r
7046   sel.cpMax = exlen;\r
7047   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7048   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7049   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7050   if (forceVisible || exlen == 0 ||\r
7051       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7052        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7053     /* Scroll to make new end of text visible if old end of text\r
7054        was visible or new text is an echo of user typein */\r
7055     sel.cpMin = 9999999;\r
7056     sel.cpMax = 9999999;\r
7057     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7058     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7059     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7060     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7061   }\r
7062   if (savesel.cpMax == exlen || forceVisible) {\r
7063     /* Move insert point to new end of text if it was at the old\r
7064        end of text or if the new text is an echo of user typein */\r
7065     sel.cpMin = 9999999;\r
7066     sel.cpMax = 9999999;\r
7067     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7068   } else {\r
7069     /* Restore previous selection */\r
7070     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7071   }\r
7072   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7073 }\r
7074 \r
7075 /*---------*/\r
7076 \r
7077 \r
7078 void\r
7079 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7080 {\r
7081   char buf[100];\r
7082   char *str;\r
7083   COLORREF oldFg, oldBg;\r
7084   HFONT oldFont;\r
7085   RECT rect;\r
7086 \r
7087   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7088 \r
7089   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7090   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7091   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7092 \r
7093   rect.left = x;\r
7094   rect.right = x + squareSize;\r
7095   rect.top  = y;\r
7096   rect.bottom = y + squareSize;\r
7097   str = buf;\r
7098 \r
7099   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7100                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7101              y, ETO_CLIPPED|ETO_OPAQUE,\r
7102              &rect, str, strlen(str), NULL);\r
7103 \r
7104   (void) SetTextColor(hdc, oldFg);\r
7105   (void) SetBkColor(hdc, oldBg);\r
7106   (void) SelectObject(hdc, oldFont);\r
7107 }\r
7108 \r
7109 void\r
7110 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7111               RECT *rect, char *color, char *flagFell)\r
7112 {\r
7113   char buf[100];\r
7114   char *str;\r
7115   COLORREF oldFg, oldBg;\r
7116   HFONT oldFont;\r
7117 \r
7118   if (appData.clockMode) {\r
7119     if (tinyLayout)\r
7120       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7121     else\r
7122       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7123     str = buf;\r
7124   } else {\r
7125     str = color;\r
7126   }\r
7127 \r
7128   if (highlight) {\r
7129     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7130     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7131   } else {\r
7132     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7133     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7134   }\r
7135   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7136 \r
7137   JAWS_SILENCE\r
7138 \r
7139   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7140              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7141              rect, str, strlen(str), NULL);\r
7142   if(logoHeight > 0 && appData.clockMode) {\r
7143       RECT r;\r
7144       sprintf(buf, "%s %s", buf+7, flagFell);\r
7145       r.top = rect->top + logoHeight/2;\r
7146       r.left = rect->left;\r
7147       r.right = rect->right;\r
7148       r.bottom = rect->bottom;\r
7149       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7150                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7151                  &r, str, strlen(str), NULL);\r
7152   }\r
7153   (void) SetTextColor(hdc, oldFg);\r
7154   (void) SetBkColor(hdc, oldBg);\r
7155   (void) SelectObject(hdc, oldFont);\r
7156 }\r
7157 \r
7158 \r
7159 int\r
7160 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7161            OVERLAPPED *ovl)\r
7162 {\r
7163   int ok, err;\r
7164 \r
7165   /* [AS]  */\r
7166   if( count <= 0 ) {\r
7167     if (appData.debugMode) {\r
7168       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7169     }\r
7170 \r
7171     return ERROR_INVALID_USER_BUFFER;\r
7172   }\r
7173 \r
7174   ResetEvent(ovl->hEvent);\r
7175   ovl->Offset = ovl->OffsetHigh = 0;\r
7176   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7177   if (ok) {\r
7178     err = NO_ERROR;\r
7179   } else {\r
7180     err = GetLastError();\r
7181     if (err == ERROR_IO_PENDING) {\r
7182       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7183       if (ok)\r
7184         err = NO_ERROR;\r
7185       else\r
7186         err = GetLastError();\r
7187     }\r
7188   }\r
7189   return err;\r
7190 }\r
7191 \r
7192 int\r
7193 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7194             OVERLAPPED *ovl)\r
7195 {\r
7196   int ok, err;\r
7197 \r
7198   ResetEvent(ovl->hEvent);\r
7199   ovl->Offset = ovl->OffsetHigh = 0;\r
7200   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7201   if (ok) {\r
7202     err = NO_ERROR;\r
7203   } else {\r
7204     err = GetLastError();\r
7205     if (err == ERROR_IO_PENDING) {\r
7206       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7207       if (ok)\r
7208         err = NO_ERROR;\r
7209       else\r
7210         err = GetLastError();\r
7211     }\r
7212   }\r
7213   return err;\r
7214 }\r
7215 \r
7216 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7217 void CheckForInputBufferFull( InputSource * is )\r
7218 {\r
7219     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7220         /* Look for end of line */\r
7221         char * p = is->buf;\r
7222         \r
7223         while( p < is->next && *p != '\n' ) {\r
7224             p++;\r
7225         }\r
7226 \r
7227         if( p >= is->next ) {\r
7228             if (appData.debugMode) {\r
7229                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7230             }\r
7231 \r
7232             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7233             is->count = (DWORD) -1;\r
7234             is->next = is->buf;\r
7235         }\r
7236     }\r
7237 }\r
7238 \r
7239 DWORD\r
7240 InputThread(LPVOID arg)\r
7241 {\r
7242   InputSource *is;\r
7243   OVERLAPPED ovl;\r
7244 \r
7245   is = (InputSource *) arg;\r
7246   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7247   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7248   while (is->hThread != NULL) {\r
7249     is->error = DoReadFile(is->hFile, is->next,\r
7250                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7251                            &is->count, &ovl);\r
7252     if (is->error == NO_ERROR) {\r
7253       is->next += is->count;\r
7254     } else {\r
7255       if (is->error == ERROR_BROKEN_PIPE) {\r
7256         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7257         is->count = 0;\r
7258       } else {\r
7259         is->count = (DWORD) -1;\r
7260         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7261         break; \r
7262       }\r
7263     }\r
7264 \r
7265     CheckForInputBufferFull( is );\r
7266 \r
7267     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7268 \r
7269     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7270 \r
7271     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7272   }\r
7273 \r
7274   CloseHandle(ovl.hEvent);\r
7275   CloseHandle(is->hFile);\r
7276 \r
7277   if (appData.debugMode) {\r
7278     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7279   }\r
7280 \r
7281   return 0;\r
7282 }\r
7283 \r
7284 \r
7285 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7286 DWORD\r
7287 NonOvlInputThread(LPVOID arg)\r
7288 {\r
7289   InputSource *is;\r
7290   char *p, *q;\r
7291   int i;\r
7292   char prev;\r
7293 \r
7294   is = (InputSource *) arg;\r
7295   while (is->hThread != NULL) {\r
7296     is->error = ReadFile(is->hFile, is->next,\r
7297                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7298                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7299     if (is->error == NO_ERROR) {\r
7300       /* Change CRLF to LF */\r
7301       if (is->next > is->buf) {\r
7302         p = is->next - 1;\r
7303         i = is->count + 1;\r
7304       } else {\r
7305         p = is->next;\r
7306         i = is->count;\r
7307       }\r
7308       q = p;\r
7309       prev = NULLCHAR;\r
7310       while (i > 0) {\r
7311         if (prev == '\r' && *p == '\n') {\r
7312           *(q-1) = '\n';\r
7313           is->count--;\r
7314         } else { \r
7315           *q++ = *p;\r
7316         }\r
7317         prev = *p++;\r
7318         i--;\r
7319       }\r
7320       *q = NULLCHAR;\r
7321       is->next = q;\r
7322     } else {\r
7323       if (is->error == ERROR_BROKEN_PIPE) {\r
7324         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7325         is->count = 0; \r
7326       } else {\r
7327         is->count = (DWORD) -1;\r
7328       }\r
7329     }\r
7330 \r
7331     CheckForInputBufferFull( is );\r
7332 \r
7333     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7334 \r
7335     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7336 \r
7337     if (is->count < 0) break;  /* Quit on error */\r
7338   }\r
7339   CloseHandle(is->hFile);\r
7340   return 0;\r
7341 }\r
7342 \r
7343 DWORD\r
7344 SocketInputThread(LPVOID arg)\r
7345 {\r
7346   InputSource *is;\r
7347 \r
7348   is = (InputSource *) arg;\r
7349   while (is->hThread != NULL) {\r
7350     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7351     if ((int)is->count == SOCKET_ERROR) {\r
7352       is->count = (DWORD) -1;\r
7353       is->error = WSAGetLastError();\r
7354     } else {\r
7355       is->error = NO_ERROR;\r
7356       is->next += is->count;\r
7357       if (is->count == 0 && is->second == is) {\r
7358         /* End of file on stderr; quit with no message */\r
7359         break;\r
7360       }\r
7361     }\r
7362     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7363 \r
7364     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7365 \r
7366     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7367   }\r
7368   return 0;\r
7369 }\r
7370 \r
7371 VOID\r
7372 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   InputSource *is;\r
7375 \r
7376   is = (InputSource *) lParam;\r
7377   if (is->lineByLine) {\r
7378     /* Feed in lines one by one */\r
7379     char *p = is->buf;\r
7380     char *q = p;\r
7381     while (q < is->next) {\r
7382       if (*q++ == '\n') {\r
7383         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7384         p = q;\r
7385       }\r
7386     }\r
7387     \r
7388     /* Move any partial line to the start of the buffer */\r
7389     q = is->buf;\r
7390     while (p < is->next) {\r
7391       *q++ = *p++;\r
7392     }\r
7393     is->next = q;\r
7394 \r
7395     if (is->error != NO_ERROR || is->count == 0) {\r
7396       /* Notify backend of the error.  Note: If there was a partial\r
7397          line at the end, it is not flushed through. */\r
7398       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7399     }\r
7400   } else {\r
7401     /* Feed in the whole chunk of input at once */\r
7402     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7403     is->next = is->buf;\r
7404   }\r
7405 }\r
7406 \r
7407 /*---------------------------------------------------------------------------*\\r
7408  *\r
7409  *  Menu enables. Used when setting various modes.\r
7410  *\r
7411 \*---------------------------------------------------------------------------*/\r
7412 \r
7413 typedef struct {\r
7414   int item;\r
7415   int flags;\r
7416 } Enables;\r
7417 \r
7418 VOID\r
7419 GreyRevert(Boolean grey)\r
7420 { // [HGM] vari: for retracting variations in local mode\r
7421   HMENU hmenu = GetMenu(hwndMain);\r
7422   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7423   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7424 }\r
7425 \r
7426 VOID\r
7427 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7428 {\r
7429   while (enab->item > 0) {\r
7430     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7431     enab++;\r
7432   }\r
7433 }\r
7434 \r
7435 Enables gnuEnables[] = {\r
7436   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7437   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7438   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7439   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7440   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7441   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7442   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7443   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7444   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7445   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7446   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7447   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7448   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7449   { -1, -1 }\r
7450 };\r
7451 \r
7452 Enables icsEnables[] = {\r
7453   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7454   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7455   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7456   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7457   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7458   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7459   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7460   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7461   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7462   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7463   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7464   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7465   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7466   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7467   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7468   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7469   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7470   { -1, -1 }\r
7471 };\r
7472 \r
7473 #if ZIPPY\r
7474 Enables zippyEnables[] = {\r
7475   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7476   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7477   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7478   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7479   { -1, -1 }\r
7480 };\r
7481 #endif\r
7482 \r
7483 Enables ncpEnables[] = {\r
7484   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7485   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7486   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7487   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7488   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7489   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7490   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7491   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7492   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7493   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7494   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7495   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7496   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7497   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7498   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7499   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7500   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7501   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7502   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7503   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7504   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7505   { -1, -1 }\r
7506 };\r
7507 \r
7508 Enables trainingOnEnables[] = {\r
7509   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7510   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7511   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7512   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7513   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7514   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7515   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7516   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7517   { -1, -1 }\r
7518 };\r
7519 \r
7520 Enables trainingOffEnables[] = {\r
7521   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7522   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7523   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7524   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7525   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7526   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7527   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7528   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7529   { -1, -1 }\r
7530 };\r
7531 \r
7532 /* These modify either ncpEnables or gnuEnables */\r
7533 Enables cmailEnables[] = {\r
7534   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7535   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7536   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7537   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7538   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7539   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7540   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7541   { -1, -1 }\r
7542 };\r
7543 \r
7544 Enables machineThinkingEnables[] = {\r
7545   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7546   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7547   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7548   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7549   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7550   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7551   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7552   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7553   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7554   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7555   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7556   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7557   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7558   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7559   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7560   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7561   { -1, -1 }\r
7562 };\r
7563 \r
7564 Enables userThinkingEnables[] = {\r
7565   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7566   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7567   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7568   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7569   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7570   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7571   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7572   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7573   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7574   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7575   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7576   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7577   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7578   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7579   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7580   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7581   { -1, -1 }\r
7582 };\r
7583 \r
7584 /*---------------------------------------------------------------------------*\\r
7585  *\r
7586  *  Front-end interface functions exported by XBoard.\r
7587  *  Functions appear in same order as prototypes in frontend.h.\r
7588  * \r
7589 \*---------------------------------------------------------------------------*/\r
7590 VOID\r
7591 ModeHighlight()\r
7592 {\r
7593   static UINT prevChecked = 0;\r
7594   static int prevPausing = 0;\r
7595   UINT nowChecked;\r
7596 \r
7597   if (pausing != prevPausing) {\r
7598     prevPausing = pausing;\r
7599     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7600                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7601     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7602   }\r
7603 \r
7604   switch (gameMode) {\r
7605   case BeginningOfGame:\r
7606     if (appData.icsActive)\r
7607       nowChecked = IDM_IcsClient;\r
7608     else if (appData.noChessProgram)\r
7609       nowChecked = IDM_EditGame;\r
7610     else\r
7611       nowChecked = IDM_MachineBlack;\r
7612     break;\r
7613   case MachinePlaysBlack:\r
7614     nowChecked = IDM_MachineBlack;\r
7615     break;\r
7616   case MachinePlaysWhite:\r
7617     nowChecked = IDM_MachineWhite;\r
7618     break;\r
7619   case TwoMachinesPlay:\r
7620     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7621     break;\r
7622   case AnalyzeMode:\r
7623     nowChecked = IDM_AnalysisMode;\r
7624     break;\r
7625   case AnalyzeFile:\r
7626     nowChecked = IDM_AnalyzeFile;\r
7627     break;\r
7628   case EditGame:\r
7629     nowChecked = IDM_EditGame;\r
7630     break;\r
7631   case PlayFromGameFile:\r
7632     nowChecked = IDM_LoadGame;\r
7633     break;\r
7634   case EditPosition:\r
7635     nowChecked = IDM_EditPosition;\r
7636     break;\r
7637   case Training:\r
7638     nowChecked = IDM_Training;\r
7639     break;\r
7640   case IcsPlayingWhite:\r
7641   case IcsPlayingBlack:\r
7642   case IcsObserving:\r
7643   case IcsIdle:\r
7644     nowChecked = IDM_IcsClient;\r
7645     break;\r
7646   default:\r
7647   case EndOfGame:\r
7648     nowChecked = 0;\r
7649     break;\r
7650   }\r
7651   if (prevChecked != 0)\r
7652     (void) CheckMenuItem(GetMenu(hwndMain),\r
7653                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7654   if (nowChecked != 0)\r
7655     (void) CheckMenuItem(GetMenu(hwndMain),\r
7656                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7657 \r
7658   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7659     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7660                           MF_BYCOMMAND|MF_ENABLED);\r
7661   } else {\r
7662     (void) EnableMenuItem(GetMenu(hwndMain), \r
7663                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7664   }\r
7665 \r
7666   prevChecked = nowChecked;\r
7667 \r
7668   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7669   if (appData.icsActive) {\r
7670        if (appData.icsEngineAnalyze) {\r
7671                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7672                        MF_BYCOMMAND|MF_CHECKED);\r
7673        } else {\r
7674                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7675                        MF_BYCOMMAND|MF_UNCHECKED);\r
7676        }\r
7677   }\r
7678 }\r
7679 \r
7680 VOID\r
7681 SetICSMode()\r
7682 {\r
7683   HMENU hmenu = GetMenu(hwndMain);\r
7684   SetMenuEnables(hmenu, icsEnables);\r
7685   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7686     MF_BYPOSITION|MF_ENABLED);\r
7687 #if ZIPPY\r
7688   if (appData.zippyPlay) {\r
7689     SetMenuEnables(hmenu, zippyEnables);\r
7690     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7691          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7692           MF_BYCOMMAND|MF_ENABLED);\r
7693   }\r
7694 #endif\r
7695 }\r
7696 \r
7697 VOID\r
7698 SetGNUMode()\r
7699 {\r
7700   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7701 }\r
7702 \r
7703 VOID\r
7704 SetNCPMode()\r
7705 {\r
7706   HMENU hmenu = GetMenu(hwndMain);\r
7707   SetMenuEnables(hmenu, ncpEnables);\r
7708   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7709     MF_BYPOSITION|MF_GRAYED);\r
7710     DrawMenuBar(hwndMain);\r
7711 }\r
7712 \r
7713 VOID\r
7714 SetCmailMode()\r
7715 {\r
7716   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7717 }\r
7718 \r
7719 VOID \r
7720 SetTrainingModeOn()\r
7721 {\r
7722   int i;\r
7723   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7724   for (i = 0; i < N_BUTTONS; i++) {\r
7725     if (buttonDesc[i].hwnd != NULL)\r
7726       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7727   }\r
7728   CommentPopDown();\r
7729 }\r
7730 \r
7731 VOID SetTrainingModeOff()\r
7732 {\r
7733   int i;\r
7734   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7735   for (i = 0; i < N_BUTTONS; i++) {\r
7736     if (buttonDesc[i].hwnd != NULL)\r
7737       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7738   }\r
7739 }\r
7740 \r
7741 \r
7742 VOID\r
7743 SetUserThinkingEnables()\r
7744 {\r
7745   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7746 }\r
7747 \r
7748 VOID\r
7749 SetMachineThinkingEnables()\r
7750 {\r
7751   HMENU hMenu = GetMenu(hwndMain);\r
7752   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7753 \r
7754   SetMenuEnables(hMenu, machineThinkingEnables);\r
7755 \r
7756   if (gameMode == MachinePlaysBlack) {\r
7757     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7758   } else if (gameMode == MachinePlaysWhite) {\r
7759     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7760   } else if (gameMode == TwoMachinesPlay) {\r
7761     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
7762   }\r
7763 }\r
7764 \r
7765 \r
7766 VOID\r
7767 DisplayTitle(char *str)\r
7768 {\r
7769   char title[MSG_SIZ], *host;\r
7770   if (str[0] != NULLCHAR) {\r
7771     strcpy(title, str);\r
7772   } else if (appData.icsActive) {\r
7773     if (appData.icsCommPort[0] != NULLCHAR)\r
7774       host = "ICS";\r
7775     else \r
7776       host = appData.icsHost;\r
7777     sprintf(title, "%s: %s", szTitle, host);\r
7778   } else if (appData.noChessProgram) {\r
7779     strcpy(title, szTitle);\r
7780   } else {\r
7781     strcpy(title, szTitle);\r
7782     strcat(title, ": ");\r
7783     strcat(title, first.tidy);\r
7784   }\r
7785   SetWindowText(hwndMain, title);\r
7786 }\r
7787 \r
7788 \r
7789 VOID\r
7790 DisplayMessage(char *str1, char *str2)\r
7791 {\r
7792   HDC hdc;\r
7793   HFONT oldFont;\r
7794   int remain = MESSAGE_TEXT_MAX - 1;\r
7795   int len;\r
7796 \r
7797   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7798   messageText[0] = NULLCHAR;\r
7799   if (*str1) {\r
7800     len = strlen(str1);\r
7801     if (len > remain) len = remain;\r
7802     strncpy(messageText, str1, len);\r
7803     messageText[len] = NULLCHAR;\r
7804     remain -= len;\r
7805   }\r
7806   if (*str2 && remain >= 2) {\r
7807     if (*str1) {\r
7808       strcat(messageText, "  ");\r
7809       remain -= 2;\r
7810     }\r
7811     len = strlen(str2);\r
7812     if (len > remain) len = remain;\r
7813     strncat(messageText, str2, len);\r
7814   }\r
7815   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7816 \r
7817   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
7818 \r
7819   SAYMACHINEMOVE();\r
7820 \r
7821   hdc = GetDC(hwndMain);\r
7822   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7823   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7824              &messageRect, messageText, strlen(messageText), NULL);\r
7825   (void) SelectObject(hdc, oldFont);\r
7826   (void) ReleaseDC(hwndMain, hdc);\r
7827 }\r
7828 \r
7829 VOID\r
7830 DisplayError(char *str, int error)\r
7831 {\r
7832   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7833   int len;\r
7834 \r
7835   if (error == 0) {\r
7836     strcpy(buf, str);\r
7837   } else {\r
7838     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7839                         NULL, error, LANG_NEUTRAL,\r
7840                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7841     if (len > 0) {\r
7842       sprintf(buf, "%s:\n%s", str, buf2);\r
7843     } else {\r
7844       ErrorMap *em = errmap;\r
7845       while (em->err != 0 && em->err != error) em++;\r
7846       if (em->err != 0) {\r
7847         sprintf(buf, "%s:\n%s", str, em->msg);\r
7848       } else {\r
7849         sprintf(buf, "%s:\nError code %d", str, error);\r
7850       }\r
7851     }\r
7852   }\r
7853   \r
7854   ErrorPopUp("Error", buf);\r
7855 }\r
7856 \r
7857 \r
7858 VOID\r
7859 DisplayMoveError(char *str)\r
7860 {\r
7861   fromX = fromY = -1;\r
7862   ClearHighlights();\r
7863   DrawPosition(FALSE, NULL);\r
7864   if (appData.popupMoveErrors) {\r
7865     ErrorPopUp("Error", str);\r
7866   } else {\r
7867     DisplayMessage(str, "");\r
7868     moveErrorMessageUp = TRUE;\r
7869   }\r
7870 }\r
7871 \r
7872 VOID\r
7873 DisplayFatalError(char *str, int error, int exitStatus)\r
7874 {\r
7875   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7876   int len;\r
7877   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7878 \r
7879   if (error != 0) {\r
7880     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7881                         NULL, error, LANG_NEUTRAL,\r
7882                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7883     if (len > 0) {\r
7884       sprintf(buf, "%s:\n%s", str, buf2);\r
7885     } else {\r
7886       ErrorMap *em = errmap;\r
7887       while (em->err != 0 && em->err != error) em++;\r
7888       if (em->err != 0) {\r
7889         sprintf(buf, "%s:\n%s", str, em->msg);\r
7890       } else {\r
7891         sprintf(buf, "%s:\nError code %d", str, error);\r
7892       }\r
7893     }\r
7894     str = buf;\r
7895   }\r
7896   if (appData.debugMode) {\r
7897     fprintf(debugFP, "%s: %s\n", label, str);\r
7898   }\r
7899   if (appData.popupExitMessage) {\r
7900     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7901                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7902   }\r
7903   ExitEvent(exitStatus);\r
7904 }\r
7905 \r
7906 \r
7907 VOID\r
7908 DisplayInformation(char *str)\r
7909 {\r
7910   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7911 }\r
7912 \r
7913 \r
7914 VOID\r
7915 DisplayNote(char *str)\r
7916 {\r
7917   ErrorPopUp("Note", str);\r
7918 }\r
7919 \r
7920 \r
7921 typedef struct {\r
7922   char *title, *question, *replyPrefix;\r
7923   ProcRef pr;\r
7924 } QuestionParams;\r
7925 \r
7926 LRESULT CALLBACK\r
7927 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7928 {\r
7929   static QuestionParams *qp;\r
7930   char reply[MSG_SIZ];\r
7931   int len, err;\r
7932 \r
7933   switch (message) {\r
7934   case WM_INITDIALOG:\r
7935     qp = (QuestionParams *) lParam;\r
7936     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7937     SetWindowText(hDlg, qp->title);\r
7938     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7939     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7940     return FALSE;\r
7941 \r
7942   case WM_COMMAND:\r
7943     switch (LOWORD(wParam)) {\r
7944     case IDOK:\r
7945       strcpy(reply, qp->replyPrefix);\r
7946       if (*reply) strcat(reply, " ");\r
7947       len = strlen(reply);\r
7948       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7949       strcat(reply, "\n");\r
7950       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7951       EndDialog(hDlg, TRUE);\r
7952       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7953       return TRUE;\r
7954     case IDCANCEL:\r
7955       EndDialog(hDlg, FALSE);\r
7956       return TRUE;\r
7957     default:\r
7958       break;\r
7959     }\r
7960     break;\r
7961   }\r
7962   return FALSE;\r
7963 }\r
7964 \r
7965 VOID\r
7966 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7967 {\r
7968     QuestionParams qp;\r
7969     FARPROC lpProc;\r
7970     \r
7971     qp.title = title;\r
7972     qp.question = question;\r
7973     qp.replyPrefix = replyPrefix;\r
7974     qp.pr = pr;\r
7975     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7976     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7977       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7978     FreeProcInstance(lpProc);\r
7979 }\r
7980 \r
7981 /* [AS] Pick FRC position */\r
7982 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7983 {\r
7984     static int * lpIndexFRC;\r
7985     BOOL index_is_ok;\r
7986     char buf[16];\r
7987 \r
7988     switch( message )\r
7989     {\r
7990     case WM_INITDIALOG:\r
7991         lpIndexFRC = (int *) lParam;\r
7992 \r
7993         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7994 \r
7995         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
7996         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
7997         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
7998         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
7999 \r
8000         break;\r
8001 \r
8002     case WM_COMMAND:\r
8003         switch( LOWORD(wParam) ) {\r
8004         case IDOK:\r
8005             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8006             EndDialog( hDlg, 0 );\r
8007             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8008             return TRUE;\r
8009         case IDCANCEL:\r
8010             EndDialog( hDlg, 1 );   \r
8011             return TRUE;\r
8012         case IDC_NFG_Edit:\r
8013             if( HIWORD(wParam) == EN_CHANGE ) {\r
8014                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8015 \r
8016                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8017             }\r
8018             return TRUE;\r
8019         case IDC_NFG_Random:\r
8020             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8021             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8022             return TRUE;\r
8023         }\r
8024 \r
8025         break;\r
8026     }\r
8027 \r
8028     return FALSE;\r
8029 }\r
8030 \r
8031 int NewGameFRC()\r
8032 {\r
8033     int result;\r
8034     int index = appData.defaultFrcPosition;\r
8035     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8036 \r
8037     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8038 \r
8039     if( result == 0 ) {\r
8040         appData.defaultFrcPosition = index;\r
8041     }\r
8042 \r
8043     return result;\r
8044 }\r
8045 \r
8046 /* [AS] Game list options. Refactored by HGM */\r
8047 \r
8048 HWND gameListOptionsDialog;\r
8049 \r
8050 // low-level front-end: clear text edit / list widget\r
8051 void\r
8052 GLT_ClearList()\r
8053 {\r
8054     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8055 }\r
8056 \r
8057 // low-level front-end: clear text edit / list widget\r
8058 void\r
8059 GLT_DeSelectList()\r
8060 {\r
8061     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8062 }\r
8063 \r
8064 // low-level front-end: append line to text edit / list widget\r
8065 void\r
8066 GLT_AddToList( char *name )\r
8067 {\r
8068     if( name != 0 ) {\r
8069             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8070     }\r
8071 }\r
8072 \r
8073 // low-level front-end: get line from text edit / list widget\r
8074 Boolean\r
8075 GLT_GetFromList( int index, char *name )\r
8076 {\r
8077     if( name != 0 ) {\r
8078             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8079                 return TRUE;\r
8080     }\r
8081     return FALSE;\r
8082 }\r
8083 \r
8084 void GLT_MoveSelection( HWND hDlg, int delta )\r
8085 {\r
8086     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8087     int idx2 = idx1 + delta;\r
8088     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8089 \r
8090     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8091         char buf[128];\r
8092 \r
8093         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8094         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8095         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8096         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8097     }\r
8098 }\r
8099 \r
8100 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8101 {\r
8102     switch( message )\r
8103     {\r
8104     case WM_INITDIALOG:\r
8105         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8106         \r
8107         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8108 \r
8109         /* Initialize list */\r
8110         GLT_TagsToList( lpUserGLT );\r
8111 \r
8112         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8113 \r
8114         break;\r
8115 \r
8116     case WM_COMMAND:\r
8117         switch( LOWORD(wParam) ) {\r
8118         case IDOK:\r
8119             GLT_ParseList();\r
8120             EndDialog( hDlg, 0 );\r
8121             return TRUE;\r
8122         case IDCANCEL:\r
8123             EndDialog( hDlg, 1 );\r
8124             return TRUE;\r
8125 \r
8126         case IDC_GLT_Default:\r
8127             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8128             return TRUE;\r
8129 \r
8130         case IDC_GLT_Restore:\r
8131             GLT_TagsToList( appData.gameListTags );\r
8132             return TRUE;\r
8133 \r
8134         case IDC_GLT_Up:\r
8135             GLT_MoveSelection( hDlg, -1 );\r
8136             return TRUE;\r
8137 \r
8138         case IDC_GLT_Down:\r
8139             GLT_MoveSelection( hDlg, +1 );\r
8140             return TRUE;\r
8141         }\r
8142 \r
8143         break;\r
8144     }\r
8145 \r
8146     return FALSE;\r
8147 }\r
8148 \r
8149 int GameListOptions()\r
8150 {\r
8151     int result;\r
8152     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8153 \r
8154     strcpy( lpUserGLT, appData.gameListTags );\r
8155 \r
8156     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8157 \r
8158     if( result == 0 ) {\r
8159         /* [AS] Memory leak here! */\r
8160         appData.gameListTags = strdup( lpUserGLT ); \r
8161     }\r
8162 \r
8163     return result;\r
8164 }\r
8165 \r
8166 VOID\r
8167 DisplayIcsInteractionTitle(char *str)\r
8168 {\r
8169   char consoleTitle[MSG_SIZ];\r
8170 \r
8171   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8172   SetWindowText(hwndConsole, consoleTitle);\r
8173 }\r
8174 \r
8175 void\r
8176 DrawPosition(int fullRedraw, Board board)\r
8177 {\r
8178   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8179 }\r
8180 \r
8181 void NotifyFrontendLogin()\r
8182 {\r
8183         if (hwndConsole)\r
8184                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8185 }\r
8186 \r
8187 VOID\r
8188 ResetFrontEnd()\r
8189 {\r
8190   fromX = fromY = -1;\r
8191   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8192     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8193     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8194     dragInfo.lastpos = dragInfo.pos;\r
8195     dragInfo.start.x = dragInfo.start.y = -1;\r
8196     dragInfo.from = dragInfo.start;\r
8197     ReleaseCapture();\r
8198     DrawPosition(TRUE, NULL);\r
8199   }\r
8200   TagsPopDown();\r
8201 }\r
8202 \r
8203 \r
8204 VOID\r
8205 CommentPopUp(char *title, char *str)\r
8206 {\r
8207   HWND hwnd = GetActiveWindow();\r
8208   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8209   SAY(str);\r
8210   SetActiveWindow(hwnd);\r
8211 }\r
8212 \r
8213 VOID\r
8214 CommentPopDown(void)\r
8215 {\r
8216   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8217   if (commentDialog) {\r
8218     ShowWindow(commentDialog, SW_HIDE);\r
8219   }\r
8220   commentUp = FALSE;\r
8221 }\r
8222 \r
8223 VOID\r
8224 EditCommentPopUp(int index, char *title, char *str)\r
8225 {\r
8226   EitherCommentPopUp(index, title, str, TRUE);\r
8227 }\r
8228 \r
8229 \r
8230 VOID\r
8231 RingBell()\r
8232 {\r
8233   MyPlaySound(&sounds[(int)SoundMove]);\r
8234 }\r
8235 \r
8236 VOID PlayIcsWinSound()\r
8237 {\r
8238   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8239 }\r
8240 \r
8241 VOID PlayIcsLossSound()\r
8242 {\r
8243   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8244 }\r
8245 \r
8246 VOID PlayIcsDrawSound()\r
8247 {\r
8248   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8249 }\r
8250 \r
8251 VOID PlayIcsUnfinishedSound()\r
8252 {\r
8253   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8254 }\r
8255 \r
8256 VOID\r
8257 PlayAlarmSound()\r
8258 {\r
8259   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8260 }\r
8261 \r
8262 \r
8263 VOID\r
8264 EchoOn()\r
8265 {\r
8266   HWND hInput;\r
8267   consoleEcho = TRUE;\r
8268   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8269   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8270   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8271 }\r
8272 \r
8273 \r
8274 VOID\r
8275 EchoOff()\r
8276 {\r
8277   CHARFORMAT cf;\r
8278   HWND hInput;\r
8279   consoleEcho = FALSE;\r
8280   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8281   /* This works OK: set text and background both to the same color */\r
8282   cf = consoleCF;\r
8283   cf.crTextColor = COLOR_ECHOOFF;\r
8284   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8285   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8286 }\r
8287 \r
8288 /* No Raw()...? */\r
8289 \r
8290 void Colorize(ColorClass cc, int continuation)\r
8291 {\r
8292   currentColorClass = cc;\r
8293   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8294   consoleCF.crTextColor = textAttribs[cc].color;\r
8295   consoleCF.dwEffects = textAttribs[cc].effects;\r
8296   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8297 }\r
8298 \r
8299 char *\r
8300 UserName()\r
8301 {\r
8302   static char buf[MSG_SIZ];\r
8303   DWORD bufsiz = MSG_SIZ;\r
8304 \r
8305   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8306         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8307   }\r
8308   if (!GetUserName(buf, &bufsiz)) {\r
8309     /*DisplayError("Error getting user name", GetLastError());*/\r
8310     strcpy(buf, "User");\r
8311   }\r
8312   return buf;\r
8313 }\r
8314 \r
8315 char *\r
8316 HostName()\r
8317 {\r
8318   static char buf[MSG_SIZ];\r
8319   DWORD bufsiz = MSG_SIZ;\r
8320 \r
8321   if (!GetComputerName(buf, &bufsiz)) {\r
8322     /*DisplayError("Error getting host name", GetLastError());*/\r
8323     strcpy(buf, "Unknown");\r
8324   }\r
8325   return buf;\r
8326 }\r
8327 \r
8328 \r
8329 int\r
8330 ClockTimerRunning()\r
8331 {\r
8332   return clockTimerEvent != 0;\r
8333 }\r
8334 \r
8335 int\r
8336 StopClockTimer()\r
8337 {\r
8338   if (clockTimerEvent == 0) return FALSE;\r
8339   KillTimer(hwndMain, clockTimerEvent);\r
8340   clockTimerEvent = 0;\r
8341   return TRUE;\r
8342 }\r
8343 \r
8344 void\r
8345 StartClockTimer(long millisec)\r
8346 {\r
8347   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8348                              (UINT) millisec, NULL);\r
8349 }\r
8350 \r
8351 void\r
8352 DisplayWhiteClock(long timeRemaining, int highlight)\r
8353 {\r
8354   HDC hdc;\r
8355   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8356 \r
8357   if(appData.noGUI) return;\r
8358   hdc = GetDC(hwndMain);\r
8359   if (!IsIconic(hwndMain)) {\r
8360     DisplayAClock(hdc, timeRemaining, highlight, \r
8361                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
8362   }\r
8363   if (highlight && iconCurrent == iconBlack) {\r
8364     iconCurrent = iconWhite;\r
8365     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8366     if (IsIconic(hwndMain)) {\r
8367       DrawIcon(hdc, 2, 2, iconCurrent);\r
8368     }\r
8369   }\r
8370   (void) ReleaseDC(hwndMain, hdc);\r
8371   if (hwndConsole)\r
8372     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8373 }\r
8374 \r
8375 void\r
8376 DisplayBlackClock(long timeRemaining, int highlight)\r
8377 {\r
8378   HDC hdc;\r
8379   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8380 \r
8381   if(appData.noGUI) return;\r
8382   hdc = GetDC(hwndMain);\r
8383   if (!IsIconic(hwndMain)) {\r
8384     DisplayAClock(hdc, timeRemaining, highlight, \r
8385                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8386   }\r
8387   if (highlight && iconCurrent == iconWhite) {\r
8388     iconCurrent = iconBlack;\r
8389     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8390     if (IsIconic(hwndMain)) {\r
8391       DrawIcon(hdc, 2, 2, iconCurrent);\r
8392     }\r
8393   }\r
8394   (void) ReleaseDC(hwndMain, hdc);\r
8395   if (hwndConsole)\r
8396     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8397 }\r
8398 \r
8399 \r
8400 int\r
8401 LoadGameTimerRunning()\r
8402 {\r
8403   return loadGameTimerEvent != 0;\r
8404 }\r
8405 \r
8406 int\r
8407 StopLoadGameTimer()\r
8408 {\r
8409   if (loadGameTimerEvent == 0) return FALSE;\r
8410   KillTimer(hwndMain, loadGameTimerEvent);\r
8411   loadGameTimerEvent = 0;\r
8412   return TRUE;\r
8413 }\r
8414 \r
8415 void\r
8416 StartLoadGameTimer(long millisec)\r
8417 {\r
8418   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8419                                 (UINT) millisec, NULL);\r
8420 }\r
8421 \r
8422 void\r
8423 AutoSaveGame()\r
8424 {\r
8425   char *defName;\r
8426   FILE *f;\r
8427   char fileTitle[MSG_SIZ];\r
8428 \r
8429   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8430   f = OpenFileDialog(hwndMain, "a", defName,\r
8431                      appData.oldSaveStyle ? "gam" : "pgn",\r
8432                      GAME_FILT, \r
8433                      "Save Game to File", NULL, fileTitle, NULL);\r
8434   if (f != NULL) {\r
8435     SaveGame(f, 0, "");\r
8436     fclose(f);\r
8437   }\r
8438 }\r
8439 \r
8440 \r
8441 void\r
8442 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8443 {\r
8444   if (delayedTimerEvent != 0) {\r
8445     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8446       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8447     }\r
8448     KillTimer(hwndMain, delayedTimerEvent);\r
8449     delayedTimerEvent = 0;\r
8450     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8451     delayedTimerCallback();\r
8452   }\r
8453   delayedTimerCallback = cb;\r
8454   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8455                                 (UINT) millisec, NULL);\r
8456 }\r
8457 \r
8458 DelayedEventCallback\r
8459 GetDelayedEvent()\r
8460 {\r
8461   if (delayedTimerEvent) {\r
8462     return delayedTimerCallback;\r
8463   } else {\r
8464     return NULL;\r
8465   }\r
8466 }\r
8467 \r
8468 void\r
8469 CancelDelayedEvent()\r
8470 {\r
8471   if (delayedTimerEvent) {\r
8472     KillTimer(hwndMain, delayedTimerEvent);\r
8473     delayedTimerEvent = 0;\r
8474   }\r
8475 }\r
8476 \r
8477 DWORD GetWin32Priority(int nice)\r
8478 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8479 /*\r
8480 REALTIME_PRIORITY_CLASS     0x00000100\r
8481 HIGH_PRIORITY_CLASS         0x00000080\r
8482 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8483 NORMAL_PRIORITY_CLASS       0x00000020\r
8484 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8485 IDLE_PRIORITY_CLASS         0x00000040\r
8486 */\r
8487         if (nice < -15) return 0x00000080;\r
8488         if (nice < 0)   return 0x00008000;\r
8489         if (nice == 0)  return 0x00000020;\r
8490         if (nice < 15)  return 0x00004000;\r
8491         return 0x00000040;\r
8492 }\r
8493 \r
8494 /* Start a child process running the given program.\r
8495    The process's standard output can be read from "from", and its\r
8496    standard input can be written to "to".\r
8497    Exit with fatal error if anything goes wrong.\r
8498    Returns an opaque pointer that can be used to destroy the process\r
8499    later.\r
8500 */\r
8501 int\r
8502 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8503 {\r
8504 #define BUFSIZE 4096\r
8505 \r
8506   HANDLE hChildStdinRd, hChildStdinWr,\r
8507     hChildStdoutRd, hChildStdoutWr;\r
8508   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8509   SECURITY_ATTRIBUTES saAttr;\r
8510   BOOL fSuccess;\r
8511   PROCESS_INFORMATION piProcInfo;\r
8512   STARTUPINFO siStartInfo;\r
8513   ChildProc *cp;\r
8514   char buf[MSG_SIZ];\r
8515   DWORD err;\r
8516 \r
8517   if (appData.debugMode) {\r
8518     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8519   }\r
8520 \r
8521   *pr = NoProc;\r
8522 \r
8523   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8524   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8525   saAttr.bInheritHandle = TRUE;\r
8526   saAttr.lpSecurityDescriptor = NULL;\r
8527 \r
8528   /*\r
8529    * The steps for redirecting child's STDOUT:\r
8530    *     1. Create anonymous pipe to be STDOUT for child.\r
8531    *     2. Create a noninheritable duplicate of read handle,\r
8532    *         and close the inheritable read handle.\r
8533    */\r
8534 \r
8535   /* Create a pipe for the child's STDOUT. */\r
8536   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8537     return GetLastError();\r
8538   }\r
8539 \r
8540   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8541   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8542                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8543                              FALSE,     /* not inherited */\r
8544                              DUPLICATE_SAME_ACCESS);\r
8545   if (! fSuccess) {\r
8546     return GetLastError();\r
8547   }\r
8548   CloseHandle(hChildStdoutRd);\r
8549 \r
8550   /*\r
8551    * The steps for redirecting child's STDIN:\r
8552    *     1. Create anonymous pipe to be STDIN for child.\r
8553    *     2. Create a noninheritable duplicate of write handle,\r
8554    *         and close the inheritable write handle.\r
8555    */\r
8556 \r
8557   /* Create a pipe for the child's STDIN. */\r
8558   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8559     return GetLastError();\r
8560   }\r
8561 \r
8562   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8563   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8564                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8565                              FALSE,     /* not inherited */\r
8566                              DUPLICATE_SAME_ACCESS);\r
8567   if (! fSuccess) {\r
8568     return GetLastError();\r
8569   }\r
8570   CloseHandle(hChildStdinWr);\r
8571 \r
8572   /* Arrange to (1) look in dir for the child .exe file, and\r
8573    * (2) have dir be the child's working directory.  Interpret\r
8574    * dir relative to the directory WinBoard loaded from. */\r
8575   GetCurrentDirectory(MSG_SIZ, buf);\r
8576   SetCurrentDirectory(installDir);\r
8577   SetCurrentDirectory(dir);\r
8578 \r
8579   /* Now create the child process. */\r
8580 \r
8581   siStartInfo.cb = sizeof(STARTUPINFO);\r
8582   siStartInfo.lpReserved = NULL;\r
8583   siStartInfo.lpDesktop = NULL;\r
8584   siStartInfo.lpTitle = NULL;\r
8585   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8586   siStartInfo.cbReserved2 = 0;\r
8587   siStartInfo.lpReserved2 = NULL;\r
8588   siStartInfo.hStdInput = hChildStdinRd;\r
8589   siStartInfo.hStdOutput = hChildStdoutWr;\r
8590   siStartInfo.hStdError = hChildStdoutWr;\r
8591 \r
8592   fSuccess = CreateProcess(NULL,\r
8593                            cmdLine,        /* command line */\r
8594                            NULL,           /* process security attributes */\r
8595                            NULL,           /* primary thread security attrs */\r
8596                            TRUE,           /* handles are inherited */\r
8597                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8598                            NULL,           /* use parent's environment */\r
8599                            NULL,\r
8600                            &siStartInfo, /* STARTUPINFO pointer */\r
8601                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8602 \r
8603   err = GetLastError();\r
8604   SetCurrentDirectory(buf); /* return to prev directory */\r
8605   if (! fSuccess) {\r
8606     return err;\r
8607   }\r
8608 \r
8609   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8610     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8611     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8612   }\r
8613 \r
8614   /* Close the handles we don't need in the parent */\r
8615   CloseHandle(piProcInfo.hThread);\r
8616   CloseHandle(hChildStdinRd);\r
8617   CloseHandle(hChildStdoutWr);\r
8618 \r
8619   /* Prepare return value */\r
8620   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8621   cp->kind = CPReal;\r
8622   cp->hProcess = piProcInfo.hProcess;\r
8623   cp->pid = piProcInfo.dwProcessId;\r
8624   cp->hFrom = hChildStdoutRdDup;\r
8625   cp->hTo = hChildStdinWrDup;\r
8626 \r
8627   *pr = (void *) cp;\r
8628 \r
8629   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8630      2000 where engines sometimes don't see the initial command(s)\r
8631      from WinBoard and hang.  I don't understand how that can happen,\r
8632      but the Sleep is harmless, so I've put it in.  Others have also\r
8633      reported what may be the same problem, so hopefully this will fix\r
8634      it for them too.  */\r
8635   Sleep(500);\r
8636 \r
8637   return NO_ERROR;\r
8638 }\r
8639 \r
8640 \r
8641 void\r
8642 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8643 {\r
8644   ChildProc *cp; int result;\r
8645 \r
8646   cp = (ChildProc *) pr;\r
8647   if (cp == NULL) return;\r
8648 \r
8649   switch (cp->kind) {\r
8650   case CPReal:\r
8651     /* TerminateProcess is considered harmful, so... */\r
8652     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8653     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8654     /* The following doesn't work because the chess program\r
8655        doesn't "have the same console" as WinBoard.  Maybe\r
8656        we could arrange for this even though neither WinBoard\r
8657        nor the chess program uses a console for stdio? */\r
8658     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8659 \r
8660     /* [AS] Special termination modes for misbehaving programs... */\r
8661     if( signal == 9 ) { \r
8662         result = TerminateProcess( cp->hProcess, 0 );\r
8663 \r
8664         if ( appData.debugMode) {\r
8665             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8666         }\r
8667     }\r
8668     else if( signal == 10 ) {\r
8669         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8670 \r
8671         if( dw != WAIT_OBJECT_0 ) {\r
8672             result = TerminateProcess( cp->hProcess, 0 );\r
8673 \r
8674             if ( appData.debugMode) {\r
8675                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8676             }\r
8677 \r
8678         }\r
8679     }\r
8680 \r
8681     CloseHandle(cp->hProcess);\r
8682     break;\r
8683 \r
8684   case CPComm:\r
8685     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8686     break;\r
8687 \r
8688   case CPSock:\r
8689     closesocket(cp->sock);\r
8690     WSACleanup();\r
8691     break;\r
8692 \r
8693   case CPRcmd:\r
8694     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8695     closesocket(cp->sock);\r
8696     closesocket(cp->sock2);\r
8697     WSACleanup();\r
8698     break;\r
8699   }\r
8700   free(cp);\r
8701 }\r
8702 \r
8703 void\r
8704 InterruptChildProcess(ProcRef pr)\r
8705 {\r
8706   ChildProc *cp;\r
8707 \r
8708   cp = (ChildProc *) pr;\r
8709   if (cp == NULL) return;\r
8710   switch (cp->kind) {\r
8711   case CPReal:\r
8712     /* The following doesn't work because the chess program\r
8713        doesn't "have the same console" as WinBoard.  Maybe\r
8714        we could arrange for this even though neither WinBoard\r
8715        nor the chess program uses a console for stdio */\r
8716     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8717     break;\r
8718 \r
8719   case CPComm:\r
8720   case CPSock:\r
8721     /* Can't interrupt */\r
8722     break;\r
8723 \r
8724   case CPRcmd:\r
8725     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8726     break;\r
8727   }\r
8728 }\r
8729 \r
8730 \r
8731 int\r
8732 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8733 {\r
8734   char cmdLine[MSG_SIZ];\r
8735 \r
8736   if (port[0] == NULLCHAR) {\r
8737     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8738   } else {\r
8739     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8740   }\r
8741   return StartChildProcess(cmdLine, "", pr);\r
8742 }\r
8743 \r
8744 \r
8745 /* Code to open TCP sockets */\r
8746 \r
8747 int\r
8748 OpenTCP(char *host, char *port, ProcRef *pr)\r
8749 {\r
8750   ChildProc *cp;\r
8751   int err;\r
8752   SOCKET s;\r
8753   struct sockaddr_in sa, mysa;\r
8754   struct hostent FAR *hp;\r
8755   unsigned short uport;\r
8756   WORD wVersionRequested;\r
8757   WSADATA wsaData;\r
8758 \r
8759   /* Initialize socket DLL */\r
8760   wVersionRequested = MAKEWORD(1, 1);\r
8761   err = WSAStartup(wVersionRequested, &wsaData);\r
8762   if (err != 0) return err;\r
8763 \r
8764   /* Make socket */\r
8765   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8766     err = WSAGetLastError();\r
8767     WSACleanup();\r
8768     return err;\r
8769   }\r
8770 \r
8771   /* Bind local address using (mostly) don't-care values.\r
8772    */\r
8773   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8774   mysa.sin_family = AF_INET;\r
8775   mysa.sin_addr.s_addr = INADDR_ANY;\r
8776   uport = (unsigned short) 0;\r
8777   mysa.sin_port = htons(uport);\r
8778   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8779       == SOCKET_ERROR) {\r
8780     err = WSAGetLastError();\r
8781     WSACleanup();\r
8782     return err;\r
8783   }\r
8784 \r
8785   /* Resolve remote host name */\r
8786   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8787   if (!(hp = gethostbyname(host))) {\r
8788     unsigned int b0, b1, b2, b3;\r
8789 \r
8790     err = WSAGetLastError();\r
8791 \r
8792     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8793       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8794       hp->h_addrtype = AF_INET;\r
8795       hp->h_length = 4;\r
8796       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8797       hp->h_addr_list[0] = (char *) malloc(4);\r
8798       hp->h_addr_list[0][0] = (char) b0;\r
8799       hp->h_addr_list[0][1] = (char) b1;\r
8800       hp->h_addr_list[0][2] = (char) b2;\r
8801       hp->h_addr_list[0][3] = (char) b3;\r
8802     } else {\r
8803       WSACleanup();\r
8804       return err;\r
8805     }\r
8806   }\r
8807   sa.sin_family = hp->h_addrtype;\r
8808   uport = (unsigned short) atoi(port);\r
8809   sa.sin_port = htons(uport);\r
8810   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8811 \r
8812   /* Make connection */\r
8813   if (connect(s, (struct sockaddr *) &sa,\r
8814               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8815     err = WSAGetLastError();\r
8816     WSACleanup();\r
8817     return err;\r
8818   }\r
8819 \r
8820   /* Prepare return value */\r
8821   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8822   cp->kind = CPSock;\r
8823   cp->sock = s;\r
8824   *pr = (ProcRef *) cp;\r
8825 \r
8826   return NO_ERROR;\r
8827 }\r
8828 \r
8829 int\r
8830 OpenCommPort(char *name, ProcRef *pr)\r
8831 {\r
8832   HANDLE h;\r
8833   COMMTIMEOUTS ct;\r
8834   ChildProc *cp;\r
8835   char fullname[MSG_SIZ];\r
8836 \r
8837   if (*name != '\\')\r
8838     sprintf(fullname, "\\\\.\\%s", name);\r
8839   else\r
8840     strcpy(fullname, name);\r
8841 \r
8842   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8843                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8844   if (h == (HANDLE) -1) {\r
8845     return GetLastError();\r
8846   }\r
8847   hCommPort = h;\r
8848 \r
8849   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8850 \r
8851   /* Accumulate characters until a 100ms pause, then parse */\r
8852   ct.ReadIntervalTimeout = 100;\r
8853   ct.ReadTotalTimeoutMultiplier = 0;\r
8854   ct.ReadTotalTimeoutConstant = 0;\r
8855   ct.WriteTotalTimeoutMultiplier = 0;\r
8856   ct.WriteTotalTimeoutConstant = 0;\r
8857   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8858 \r
8859   /* Prepare return value */\r
8860   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8861   cp->kind = CPComm;\r
8862   cp->hFrom = h;\r
8863   cp->hTo = h;\r
8864   *pr = (ProcRef *) cp;\r
8865 \r
8866   return NO_ERROR;\r
8867 }\r
8868 \r
8869 int\r
8870 OpenLoopback(ProcRef *pr)\r
8871 {\r
8872   DisplayFatalError("Not implemented", 0, 1);\r
8873   return NO_ERROR;\r
8874 }\r
8875 \r
8876 \r
8877 int\r
8878 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8879 {\r
8880   ChildProc *cp;\r
8881   int err;\r
8882   SOCKET s, s2, s3;\r
8883   struct sockaddr_in sa, mysa;\r
8884   struct hostent FAR *hp;\r
8885   unsigned short uport;\r
8886   WORD wVersionRequested;\r
8887   WSADATA wsaData;\r
8888   int fromPort;\r
8889   char stderrPortStr[MSG_SIZ];\r
8890 \r
8891   /* Initialize socket DLL */\r
8892   wVersionRequested = MAKEWORD(1, 1);\r
8893   err = WSAStartup(wVersionRequested, &wsaData);\r
8894   if (err != 0) return err;\r
8895 \r
8896   /* Resolve remote host name */\r
8897   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8898   if (!(hp = gethostbyname(host))) {\r
8899     unsigned int b0, b1, b2, b3;\r
8900 \r
8901     err = WSAGetLastError();\r
8902 \r
8903     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8904       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8905       hp->h_addrtype = AF_INET;\r
8906       hp->h_length = 4;\r
8907       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8908       hp->h_addr_list[0] = (char *) malloc(4);\r
8909       hp->h_addr_list[0][0] = (char) b0;\r
8910       hp->h_addr_list[0][1] = (char) b1;\r
8911       hp->h_addr_list[0][2] = (char) b2;\r
8912       hp->h_addr_list[0][3] = (char) b3;\r
8913     } else {\r
8914       WSACleanup();\r
8915       return err;\r
8916     }\r
8917   }\r
8918   sa.sin_family = hp->h_addrtype;\r
8919   uport = (unsigned short) 514;\r
8920   sa.sin_port = htons(uport);\r
8921   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8922 \r
8923   /* Bind local socket to unused "privileged" port address\r
8924    */\r
8925   s = INVALID_SOCKET;\r
8926   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8927   mysa.sin_family = AF_INET;\r
8928   mysa.sin_addr.s_addr = INADDR_ANY;\r
8929   for (fromPort = 1023;; fromPort--) {\r
8930     if (fromPort < 0) {\r
8931       WSACleanup();\r
8932       return WSAEADDRINUSE;\r
8933     }\r
8934     if (s == INVALID_SOCKET) {\r
8935       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8936         err = WSAGetLastError();\r
8937         WSACleanup();\r
8938         return err;\r
8939       }\r
8940     }\r
8941     uport = (unsigned short) fromPort;\r
8942     mysa.sin_port = htons(uport);\r
8943     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8944         == SOCKET_ERROR) {\r
8945       err = WSAGetLastError();\r
8946       if (err == WSAEADDRINUSE) continue;\r
8947       WSACleanup();\r
8948       return err;\r
8949     }\r
8950     if (connect(s, (struct sockaddr *) &sa,\r
8951       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8952       err = WSAGetLastError();\r
8953       if (err == WSAEADDRINUSE) {\r
8954         closesocket(s);\r
8955         s = -1;\r
8956         continue;\r
8957       }\r
8958       WSACleanup();\r
8959       return err;\r
8960     }\r
8961     break;\r
8962   }\r
8963 \r
8964   /* Bind stderr local socket to unused "privileged" port address\r
8965    */\r
8966   s2 = INVALID_SOCKET;\r
8967   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8968   mysa.sin_family = AF_INET;\r
8969   mysa.sin_addr.s_addr = INADDR_ANY;\r
8970   for (fromPort = 1023;; fromPort--) {\r
8971     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8972     if (fromPort < 0) {\r
8973       (void) closesocket(s);\r
8974       WSACleanup();\r
8975       return WSAEADDRINUSE;\r
8976     }\r
8977     if (s2 == INVALID_SOCKET) {\r
8978       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8979         err = WSAGetLastError();\r
8980         closesocket(s);\r
8981         WSACleanup();\r
8982         return err;\r
8983       }\r
8984     }\r
8985     uport = (unsigned short) fromPort;\r
8986     mysa.sin_port = htons(uport);\r
8987     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8988         == SOCKET_ERROR) {\r
8989       err = WSAGetLastError();\r
8990       if (err == WSAEADDRINUSE) continue;\r
8991       (void) closesocket(s);\r
8992       WSACleanup();\r
8993       return err;\r
8994     }\r
8995     if (listen(s2, 1) == SOCKET_ERROR) {\r
8996       err = WSAGetLastError();\r
8997       if (err == WSAEADDRINUSE) {\r
8998         closesocket(s2);\r
8999         s2 = INVALID_SOCKET;\r
9000         continue;\r
9001       }\r
9002       (void) closesocket(s);\r
9003       (void) closesocket(s2);\r
9004       WSACleanup();\r
9005       return err;\r
9006     }\r
9007     break;\r
9008   }\r
9009   prevStderrPort = fromPort; // remember port used\r
9010   sprintf(stderrPortStr, "%d", fromPort);\r
9011 \r
9012   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9013     err = WSAGetLastError();\r
9014     (void) closesocket(s);\r
9015     (void) closesocket(s2);\r
9016     WSACleanup();\r
9017     return err;\r
9018   }\r
9019 \r
9020   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9021     err = WSAGetLastError();\r
9022     (void) closesocket(s);\r
9023     (void) closesocket(s2);\r
9024     WSACleanup();\r
9025     return err;\r
9026   }\r
9027   if (*user == NULLCHAR) user = UserName();\r
9028   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9029     err = WSAGetLastError();\r
9030     (void) closesocket(s);\r
9031     (void) closesocket(s2);\r
9032     WSACleanup();\r
9033     return err;\r
9034   }\r
9035   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9036     err = WSAGetLastError();\r
9037     (void) closesocket(s);\r
9038     (void) closesocket(s2);\r
9039     WSACleanup();\r
9040     return err;\r
9041   }\r
9042 \r
9043   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9044     err = WSAGetLastError();\r
9045     (void) closesocket(s);\r
9046     (void) closesocket(s2);\r
9047     WSACleanup();\r
9048     return err;\r
9049   }\r
9050   (void) closesocket(s2);  /* Stop listening */\r
9051 \r
9052   /* Prepare return value */\r
9053   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9054   cp->kind = CPRcmd;\r
9055   cp->sock = s;\r
9056   cp->sock2 = s3;\r
9057   *pr = (ProcRef *) cp;\r
9058 \r
9059   return NO_ERROR;\r
9060 }\r
9061 \r
9062 \r
9063 InputSourceRef\r
9064 AddInputSource(ProcRef pr, int lineByLine,\r
9065                InputCallback func, VOIDSTAR closure)\r
9066 {\r
9067   InputSource *is, *is2 = NULL;\r
9068   ChildProc *cp = (ChildProc *) pr;\r
9069 \r
9070   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9071   is->lineByLine = lineByLine;\r
9072   is->func = func;\r
9073   is->closure = closure;\r
9074   is->second = NULL;\r
9075   is->next = is->buf;\r
9076   if (pr == NoProc) {\r
9077     is->kind = CPReal;\r
9078     consoleInputSource = is;\r
9079   } else {\r
9080     is->kind = cp->kind;\r
9081     /* \r
9082         [AS] Try to avoid a race condition if the thread is given control too early:\r
9083         we create all threads suspended so that the is->hThread variable can be\r
9084         safely assigned, then let the threads start with ResumeThread.\r
9085     */\r
9086     switch (cp->kind) {\r
9087     case CPReal:\r
9088       is->hFile = cp->hFrom;\r
9089       cp->hFrom = NULL; /* now owned by InputThread */\r
9090       is->hThread =\r
9091         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9092                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9093       break;\r
9094 \r
9095     case CPComm:\r
9096       is->hFile = cp->hFrom;\r
9097       cp->hFrom = NULL; /* now owned by InputThread */\r
9098       is->hThread =\r
9099         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9100                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9101       break;\r
9102 \r
9103     case CPSock:\r
9104       is->sock = cp->sock;\r
9105       is->hThread =\r
9106         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9107                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9108       break;\r
9109 \r
9110     case CPRcmd:\r
9111       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9112       *is2 = *is;\r
9113       is->sock = cp->sock;\r
9114       is->second = is2;\r
9115       is2->sock = cp->sock2;\r
9116       is2->second = is2;\r
9117       is->hThread =\r
9118         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9119                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9120       is2->hThread =\r
9121         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9122                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9123       break;\r
9124     }\r
9125 \r
9126     if( is->hThread != NULL ) {\r
9127         ResumeThread( is->hThread );\r
9128     }\r
9129 \r
9130     if( is2 != NULL && is2->hThread != NULL ) {\r
9131         ResumeThread( is2->hThread );\r
9132     }\r
9133   }\r
9134 \r
9135   return (InputSourceRef) is;\r
9136 }\r
9137 \r
9138 void\r
9139 RemoveInputSource(InputSourceRef isr)\r
9140 {\r
9141   InputSource *is;\r
9142 \r
9143   is = (InputSource *) isr;\r
9144   is->hThread = NULL;  /* tell thread to stop */\r
9145   CloseHandle(is->hThread);\r
9146   if (is->second != NULL) {\r
9147     is->second->hThread = NULL;\r
9148     CloseHandle(is->second->hThread);\r
9149   }\r
9150 }\r
9151 \r
9152 int no_wrap(char *message, int count)\r
9153 {\r
9154     ConsoleOutput(message, count, FALSE);\r
9155     return count;\r
9156 }\r
9157 \r
9158 int\r
9159 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9160 {\r
9161   DWORD dOutCount;\r
9162   int outCount = SOCKET_ERROR;\r
9163   ChildProc *cp = (ChildProc *) pr;\r
9164   static OVERLAPPED ovl;\r
9165   static int line = 0;\r
9166 \r
9167   if (pr == NoProc)\r
9168   {\r
9169     if (appData.noJoin || !appData.useInternalWrap)\r
9170       return no_wrap(message, count);\r
9171     else\r
9172     {\r
9173       int width = get_term_width();\r
9174       int len = wrap(NULL, message, count, width, &line);\r
9175       char *msg = malloc(len);\r
9176       int dbgchk;\r
9177 \r
9178       if (!msg)\r
9179         return no_wrap(message, count);\r
9180       else\r
9181       {\r
9182         dbgchk = wrap(msg, message, count, width, &line);\r
9183         if (dbgchk != len && appData.debugMode)\r
9184             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9185         ConsoleOutput(msg, len, FALSE);\r
9186         free(msg);\r
9187         return len;\r
9188       }\r
9189     }\r
9190   }\r
9191 \r
9192   if (ovl.hEvent == NULL) {\r
9193     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9194   }\r
9195   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9196 \r
9197   switch (cp->kind) {\r
9198   case CPSock:\r
9199   case CPRcmd:\r
9200     outCount = send(cp->sock, message, count, 0);\r
9201     if (outCount == SOCKET_ERROR) {\r
9202       *outError = WSAGetLastError();\r
9203     } else {\r
9204       *outError = NO_ERROR;\r
9205     }\r
9206     break;\r
9207 \r
9208   case CPReal:\r
9209     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9210                   &dOutCount, NULL)) {\r
9211       *outError = NO_ERROR;\r
9212       outCount = (int) dOutCount;\r
9213     } else {\r
9214       *outError = GetLastError();\r
9215     }\r
9216     break;\r
9217 \r
9218   case CPComm:\r
9219     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9220                             &dOutCount, &ovl);\r
9221     if (*outError == NO_ERROR) {\r
9222       outCount = (int) dOutCount;\r
9223     }\r
9224     break;\r
9225   }\r
9226   return outCount;\r
9227 }\r
9228 \r
9229 int\r
9230 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9231                        long msdelay)\r
9232 {\r
9233   /* Ignore delay, not implemented for WinBoard */\r
9234   return OutputToProcess(pr, message, count, outError);\r
9235 }\r
9236 \r
9237 \r
9238 void\r
9239 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9240                         char *buf, int count, int error)\r
9241 {\r
9242   DisplayFatalError("Not implemented", 0, 1);\r
9243 }\r
9244 \r
9245 /* see wgamelist.c for Game List functions */\r
9246 /* see wedittags.c for Edit Tags functions */\r
9247 \r
9248 \r
9249 VOID\r
9250 ICSInitScript()\r
9251 {\r
9252   FILE *f;\r
9253   char buf[MSG_SIZ];\r
9254   char *dummy;\r
9255 \r
9256   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9257     f = fopen(buf, "r");\r
9258     if (f != NULL) {\r
9259       ProcessICSInitScript(f);\r
9260       fclose(f);\r
9261     }\r
9262   }\r
9263 }\r
9264 \r
9265 \r
9266 VOID\r
9267 StartAnalysisClock()\r
9268 {\r
9269   if (analysisTimerEvent) return;\r
9270   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9271                                         (UINT) 2000, NULL);\r
9272 }\r
9273 \r
9274 VOID\r
9275 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9276 {\r
9277   highlightInfo.sq[0].x = fromX;\r
9278   highlightInfo.sq[0].y = fromY;\r
9279   highlightInfo.sq[1].x = toX;\r
9280   highlightInfo.sq[1].y = toY;\r
9281 }\r
9282 \r
9283 VOID\r
9284 ClearHighlights()\r
9285 {\r
9286   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9287     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9288 }\r
9289 \r
9290 VOID\r
9291 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9292 {\r
9293   premoveHighlightInfo.sq[0].x = fromX;\r
9294   premoveHighlightInfo.sq[0].y = fromY;\r
9295   premoveHighlightInfo.sq[1].x = toX;\r
9296   premoveHighlightInfo.sq[1].y = toY;\r
9297 }\r
9298 \r
9299 VOID\r
9300 ClearPremoveHighlights()\r
9301 {\r
9302   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9303     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9304 }\r
9305 \r
9306 VOID\r
9307 ShutDownFrontEnd()\r
9308 {\r
9309   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9310   DeleteClipboardTempFiles();\r
9311 }\r
9312 \r
9313 void\r
9314 BoardToTop()\r
9315 {\r
9316     if (IsIconic(hwndMain))\r
9317       ShowWindow(hwndMain, SW_RESTORE);\r
9318 \r
9319     SetActiveWindow(hwndMain);\r
9320 }\r
9321 \r
9322 /*\r
9323  * Prototypes for animation support routines\r
9324  */\r
9325 static void ScreenSquare(int column, int row, POINT * pt);\r
9326 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9327      POINT frames[], int * nFrames);\r
9328 \r
9329 \r
9330 void\r
9331 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9332 {       // [HGM] atomic: animate blast wave\r
9333         int i;\r
9334 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9335         explodeInfo.fromX = fromX;\r
9336         explodeInfo.fromY = fromY;\r
9337         explodeInfo.toX = toX;\r
9338         explodeInfo.toY = toY;\r
9339         for(i=1; i<nFrames; i++) {\r
9340             explodeInfo.radius = (i*180)/(nFrames-1);\r
9341             DrawPosition(FALSE, NULL);\r
9342             Sleep(appData.animSpeed);\r
9343         }\r
9344         explodeInfo.radius = 0;\r
9345         DrawPosition(TRUE, NULL);\r
9346 }\r
9347 \r
9348 #define kFactor 4\r
9349 \r
9350 void\r
9351 AnimateMove(board, fromX, fromY, toX, toY)\r
9352      Board board;\r
9353      int fromX;\r
9354      int fromY;\r
9355      int toX;\r
9356      int toY;\r
9357 {\r
9358   ChessSquare piece;\r
9359   POINT start, finish, mid;\r
9360   POINT frames[kFactor * 2 + 1];\r
9361   int nFrames, n;\r
9362 \r
9363   if (!appData.animate) return;\r
9364   if (doingSizing) return;\r
9365   if (fromY < 0 || fromX < 0) return;\r
9366   piece = board[fromY][fromX];\r
9367   if (piece >= EmptySquare) return;\r
9368 \r
9369   ScreenSquare(fromX, fromY, &start);\r
9370   ScreenSquare(toX, toY, &finish);\r
9371 \r
9372   /* All pieces except knights move in straight line */\r
9373   if (piece != WhiteKnight && piece != BlackKnight) {\r
9374     mid.x = start.x + (finish.x - start.x) / 2;\r
9375     mid.y = start.y + (finish.y - start.y) / 2;\r
9376   } else {\r
9377     /* Knight: make diagonal movement then straight */\r
9378     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9379        mid.x = start.x + (finish.x - start.x) / 2;\r
9380        mid.y = finish.y;\r
9381      } else {\r
9382        mid.x = finish.x;\r
9383        mid.y = start.y + (finish.y - start.y) / 2;\r
9384      }\r
9385   }\r
9386   \r
9387   /* Don't use as many frames for very short moves */\r
9388   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9389     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9390   else\r
9391     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9392 \r
9393   animInfo.from.x = fromX;\r
9394   animInfo.from.y = fromY;\r
9395   animInfo.to.x = toX;\r
9396   animInfo.to.y = toY;\r
9397   animInfo.lastpos = start;\r
9398   animInfo.piece = piece;\r
9399   for (n = 0; n < nFrames; n++) {\r
9400     animInfo.pos = frames[n];\r
9401     DrawPosition(FALSE, NULL);\r
9402     animInfo.lastpos = animInfo.pos;\r
9403     Sleep(appData.animSpeed);\r
9404   }\r
9405   animInfo.pos = finish;\r
9406   DrawPosition(FALSE, NULL);\r
9407   animInfo.piece = EmptySquare;\r
9408   if(gameInfo.variant == VariantAtomic && \r
9409      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9410         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9411 }\r
9412 \r
9413 /*      Convert board position to corner of screen rect and color       */\r
9414 \r
9415 static void\r
9416 ScreenSquare(column, row, pt)\r
9417      int column; int row; POINT * pt;\r
9418 {\r
9419   if (flipView) {\r
9420     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9421     pt->y = lineGap + row * (squareSize + lineGap);\r
9422   } else {\r
9423     pt->x = lineGap + column * (squareSize + lineGap);\r
9424     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9425   }\r
9426 }\r
9427 \r
9428 /*      Generate a series of frame coords from start->mid->finish.\r
9429         The movement rate doubles until the half way point is\r
9430         reached, then halves back down to the final destination,\r
9431         which gives a nice slow in/out effect. The algorithmn\r
9432         may seem to generate too many intermediates for short\r
9433         moves, but remember that the purpose is to attract the\r
9434         viewers attention to the piece about to be moved and\r
9435         then to where it ends up. Too few frames would be less\r
9436         noticeable.                                             */\r
9437 \r
9438 static void\r
9439 Tween(start, mid, finish, factor, frames, nFrames)\r
9440      POINT * start; POINT * mid;\r
9441      POINT * finish; int factor;\r
9442      POINT frames[]; int * nFrames;\r
9443 {\r
9444   int n, fraction = 1, count = 0;\r
9445 \r
9446   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9447   for (n = 0; n < factor; n++)\r
9448     fraction *= 2;\r
9449   for (n = 0; n < factor; n++) {\r
9450     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9451     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9452     count ++;\r
9453     fraction = fraction / 2;\r
9454   }\r
9455   \r
9456   /* Midpoint */\r
9457   frames[count] = *mid;\r
9458   count ++;\r
9459   \r
9460   /* Slow out, stepping 1/2, then 1/4, ... */\r
9461   fraction = 2;\r
9462   for (n = 0; n < factor; n++) {\r
9463     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9464     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9465     count ++;\r
9466     fraction = fraction * 2;\r
9467   }\r
9468   *nFrames = count;\r
9469 }\r
9470 \r
9471 void\r
9472 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9473 {\r
9474     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9475 \r
9476     EvalGraphSet( first, last, current, pvInfoList );\r
9477 }\r