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