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