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