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