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