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