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