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