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