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