Harmonize declarations of XBoard and WinBoard
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P(());\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 \r
138 typedef struct { // [HGM] atomic\r
139   int fromX, fromY, toX, toY, radius;\r
140 } ExplodeInfo;\r
141 \r
142 static ExplodeInfo explodeInfo;\r
143 \r
144 /* Window class names */\r
145 char szAppName[] = "WinBoard";\r
146 char szConsoleName[] = "WBConsole";\r
147 \r
148 /* Title bar text */\r
149 char szTitle[] = "WinBoard";\r
150 char szConsoleTitle[] = "I C S Interaction";\r
151 \r
152 char *programName;\r
153 char *settingsFileName;\r
154 Boolean saveSettingsOnExit;\r
155 char installDir[MSG_SIZ];\r
156 int errorExitStatus;\r
157 \r
158 BoardSize boardSize;\r
159 Boolean chessProgram;\r
160 //static int boardX, boardY;\r
161 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
162 static int squareSize, lineGap, minorSize;\r
163 static int winW, winH;\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
165 static int logoHeight = 0;\r
166 static char messageText[MESSAGE_TEXT_MAX];\r
167 static int clockTimerEvent = 0;\r
168 static int loadGameTimerEvent = 0;\r
169 static int analysisTimerEvent = 0;\r
170 static DelayedEventCallback delayedTimerCallback;\r
171 static int delayedTimerEvent = 0;\r
172 static int buttonCount = 2;\r
173 char *icsTextMenuString;\r
174 char *icsNames;\r
175 char *firstChessProgramNames;\r
176 char *secondChessProgramNames;\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 Boolean alwaysOnTop = FALSE;\r
182 RECT boardRect;\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
184   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
185 HPALETTE hPal;\r
186 ColorClass currentColorClass;\r
187 \r
188 HWND hCommPort = NULL;    /* currently open comm port */\r
189 static HWND hwndPause;    /* pause button */\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,\r
192   blackSquareBrush, /* [HGM] for band between board and holdings */\r
193   explodeBrush,     /* [HGM] atomic */\r
194   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
195 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
196 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
197 static HPEN gridPen = NULL;\r
198 static HPEN highlightPen = NULL;\r
199 static HPEN premovePen = NULL;\r
200 static NPLOGPALETTE pLogPal;\r
201 static BOOL paletteChanged = FALSE;\r
202 static HICON iconWhite, iconBlack, iconCurrent;\r
203 static int doingSizing = FALSE;\r
204 static int lastSizing = 0;\r
205 static int prevStderrPort;\r
206 static HBITMAP userLogo;\r
207 \r
208 static HBITMAP liteBackTexture = NULL;\r
209 static HBITMAP darkBackTexture = NULL;\r
210 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
211 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
212 static int backTextureSquareSize = 0;\r
213 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
214 \r
215 #if __GNUC__ && !defined(_winmajor)\r
216 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
217 #else\r
218 #if defined(_winmajor)\r
219 #define oldDialog (_winmajor < 4)\r
220 #else\r
221 #define oldDialog 0\r
222 #endif\r
223 #endif\r
224 \r
225 typedef struct {\r
226   char *name;\r
227   int squareSize;\r
228   int lineGap;\r
229   int smallLayout;\r
230   int tinyLayout;\r
231   int cliWidth, cliHeight;\r
232 } SizeInfo;\r
233 \r
234 SizeInfo sizeInfo[] = \r
235 {\r
236   { "tiny",     21, 0, 1, 1, 0, 0 },\r
237   { "teeny",    25, 1, 1, 1, 0, 0 },\r
238   { "dinky",    29, 1, 1, 1, 0, 0 },\r
239   { "petite",   33, 1, 1, 1, 0, 0 },\r
240   { "slim",     37, 2, 1, 0, 0, 0 },\r
241   { "small",    40, 2, 1, 0, 0, 0 },\r
242   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
243   { "middling", 49, 2, 0, 0, 0, 0 },\r
244   { "average",  54, 2, 0, 0, 0, 0 },\r
245   { "moderate", 58, 3, 0, 0, 0, 0 },\r
246   { "medium",   64, 3, 0, 0, 0, 0 },\r
247   { "bulky",    72, 3, 0, 0, 0, 0 },\r
248   { "large",    80, 3, 0, 0, 0, 0 },\r
249   { "big",      87, 3, 0, 0, 0, 0 },\r
250   { "huge",     95, 3, 0, 0, 0, 0 },\r
251   { "giant",    108, 3, 0, 0, 0, 0 },\r
252   { "colossal", 116, 4, 0, 0, 0, 0 },\r
253   { "titanic",  129, 4, 0, 0, 0, 0 },\r
254   { NULL, 0, 0, 0, 0, 0, 0 }\r
255 };\r
256 \r
257 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
258 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
259 {\r
260   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
261   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
262   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
263   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
264   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
265   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
266   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
267   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
278 };\r
279 \r
280 MyFont *font[NUM_SIZES][NUM_FONTS];\r
281 \r
282 typedef struct {\r
283   char *label;\r
284   int id;\r
285   HWND hwnd;\r
286   WNDPROC wndproc;\r
287 } MyButtonDesc;\r
288 \r
289 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
290 #define N_BUTTONS 5\r
291 \r
292 MyButtonDesc buttonDesc[N_BUTTONS] =\r
293 {\r
294   {"<<", IDM_ToStart, NULL, NULL},\r
295   {"<", IDM_Backward, NULL, NULL},\r
296   {"P", IDM_Pause, NULL, NULL},\r
297   {">", IDM_Forward, NULL, NULL},\r
298   {">>", IDM_ToEnd, NULL, NULL},\r
299 };\r
300 \r
301 int tinyLayout = 0, smallLayout = 0;\r
302 #define MENU_BAR_ITEMS 7\r
303 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
304   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
305   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
306 };\r
307 \r
308 \r
309 MySound sounds[(int)NSoundClasses];\r
310 MyTextAttribs textAttribs[(int)NColorClasses];\r
311 \r
312 MyColorizeAttribs colorizeAttribs[] = {\r
313   { (COLORREF)0, 0, "Shout Text" },\r
314   { (COLORREF)0, 0, "SShout/CShout" },\r
315   { (COLORREF)0, 0, "Channel 1 Text" },\r
316   { (COLORREF)0, 0, "Channel Text" },\r
317   { (COLORREF)0, 0, "Kibitz Text" },\r
318   { (COLORREF)0, 0, "Tell Text" },\r
319   { (COLORREF)0, 0, "Challenge Text" },\r
320   { (COLORREF)0, 0, "Request Text" },\r
321   { (COLORREF)0, 0, "Seek Text" },\r
322   { (COLORREF)0, 0, "Normal Text" },\r
323   { (COLORREF)0, 0, "None" }\r
324 };\r
325 \r
326 \r
327 \r
328 static char *commentTitle;\r
329 static char *commentText;\r
330 static int commentIndex;\r
331 static Boolean editComment = FALSE;\r
332 \r
333 \r
334 char errorTitle[MSG_SIZ];\r
335 char errorMessage[2*MSG_SIZ];\r
336 HWND errorDialog = NULL;\r
337 BOOLEAN moveErrorMessageUp = FALSE;\r
338 BOOLEAN consoleEcho = TRUE;\r
339 CHARFORMAT consoleCF;\r
340 COLORREF consoleBackgroundColor;\r
341 \r
342 char *programVersion;\r
343 \r
344 #define CPReal 1\r
345 #define CPComm 2\r
346 #define CPSock 3\r
347 #define CPRcmd 4\r
348 typedef int CPKind;\r
349 \r
350 typedef struct {\r
351   CPKind kind;\r
352   HANDLE hProcess;\r
353   DWORD pid;\r
354   HANDLE hTo;\r
355   HANDLE hFrom;\r
356   SOCKET sock;\r
357   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
358 } ChildProc;\r
359 \r
360 #define INPUT_SOURCE_BUF_SIZE 4096\r
361 \r
362 typedef struct _InputSource {\r
363   CPKind kind;\r
364   HANDLE hFile;\r
365   SOCKET sock;\r
366   int lineByLine;\r
367   HANDLE hThread;\r
368   DWORD id;\r
369   char buf[INPUT_SOURCE_BUF_SIZE];\r
370   char *next;\r
371   DWORD count;\r
372   int error;\r
373   InputCallback func;\r
374   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
375   VOIDSTAR closure;\r
376 } InputSource;\r
377 \r
378 InputSource *consoleInputSource;\r
379 \r
380 DCB dcb;\r
381 \r
382 /* forward */\r
383 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
384 VOID ConsoleCreate();\r
385 LRESULT CALLBACK\r
386   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
387 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
388 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
389 VOID ParseCommSettings(char *arg, DCB *dcb);\r
390 LRESULT CALLBACK\r
391   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
392 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
393 void ParseIcsTextMenu(char *icsTextMenuString);\r
394 VOID PopUpMoveDialog(char firstchar);\r
395 VOID PopUpNameDialog(char firstchar);\r
396 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
397 \r
398 /* [AS] */\r
399 int NewGameFRC();\r
400 int GameListOptions();\r
401 \r
402 int dummy; // [HGM] for obsolete args\r
403 \r
404 HWND hwndMain = NULL;        /* root window*/\r
405 HWND hwndConsole = NULL;\r
406 HWND commentDialog = NULL;\r
407 HWND moveHistoryDialog = NULL;\r
408 HWND evalGraphDialog = NULL;\r
409 HWND engineOutputDialog = NULL;\r
410 HWND gameListDialog = NULL;\r
411 HWND editTagsDialog = NULL;\r
412 \r
413 int commentUp = FALSE;\r
414 \r
415 WindowPlacement wpMain;\r
416 WindowPlacement wpConsole;\r
417 WindowPlacement wpComment;\r
418 WindowPlacement wpMoveHistory;\r
419 WindowPlacement wpEvalGraph;\r
420 WindowPlacement wpEngineOutput;\r
421 WindowPlacement wpGameList;\r
422 WindowPlacement wpTags;\r
423 \r
424 VOID EngineOptionsPopup(); // [HGM] settings\r
425 \r
426 VOID GothicPopUp(char *title, VariantClass variant);\r
427 /*\r
428  * Setting "frozen" should disable all user input other than deleting\r
429  * the window.  We do this while engines are initializing themselves.\r
430  */\r
431 static int frozen = 0;\r
432 static int oldMenuItemState[MENU_BAR_ITEMS];\r
433 void FreezeUI()\r
434 {\r
435   HMENU hmenu;\r
436   int i;\r
437 \r
438   if (frozen) return;\r
439   frozen = 1;\r
440   hmenu = GetMenu(hwndMain);\r
441   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
442     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
443   }\r
444   DrawMenuBar(hwndMain);\r
445 }\r
446 \r
447 /* Undo a FreezeUI */\r
448 void ThawUI()\r
449 {\r
450   HMENU hmenu;\r
451   int i;\r
452 \r
453   if (!frozen) return;\r
454   frozen = 0;\r
455   hmenu = GetMenu(hwndMain);\r
456   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
457     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
458   }\r
459   DrawMenuBar(hwndMain);\r
460 }\r
461 \r
462 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
463 \r
464 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
465 #ifdef JAWS\r
466 #include "jaws.c"\r
467 #else\r
468 #define JAWS_INIT\r
469 #define JAWS_ARGS\r
470 #define JAWS_ALT_INTERCEPT\r
471 #define JAWS_KB_NAVIGATION\r
472 #define JAWS_MENU_ITEMS\r
473 #define JAWS_SILENCE\r
474 #define JAWS_REPLAY\r
475 #define JAWS_ACCEL\r
476 #define JAWS_COPYRIGHT\r
477 #define JAWS_DELETE(X) X\r
478 #define SAYMACHINEMOVE()\r
479 #define SAY(X)\r
480 #endif\r
481 \r
482 /*---------------------------------------------------------------------------*\\r
483  *\r
484  * WinMain\r
485  *\r
486 \*---------------------------------------------------------------------------*/\r
487 \r
488 int APIENTRY\r
489 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
490         LPSTR lpCmdLine, int nCmdShow)\r
491 {\r
492   MSG msg;\r
493   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
494 //  INITCOMMONCONTROLSEX ex;\r
495 \r
496   debugFP = stderr;\r
497 \r
498   LoadLibrary("RICHED32.DLL");\r
499   consoleCF.cbSize = sizeof(CHARFORMAT);\r
500 \r
501   if (!InitApplication(hInstance)) {\r
502     return (FALSE);\r
503   }\r
504   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
505     return (FALSE);\r
506   }\r
507 \r
508   JAWS_INIT\r
509 \r
510 //  InitCommonControlsEx(&ex);\r
511   InitCommonControls();\r
512 \r
513   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
514   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
515   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
516 \r
517   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
518 \r
519   while (GetMessage(&msg, /* message structure */\r
520                     NULL, /* handle of window receiving the message */\r
521                     0,    /* lowest message to examine */\r
522                     0))   /* highest message to examine */\r
523     {\r
524 \r
525       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
526         // [HGM] navigate: switch between all windows with tab\r
527         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
528         int i, currentElement = 0;\r
529 \r
530         // first determine what element of the chain we come from (if any)\r
531         if(appData.icsActive) {\r
532             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
533             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
534         }\r
535         if(engineOutputDialog && EngineOutputIsUp()) {\r
536             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
537             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
538         }\r
539         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
540             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
541         }\r
542         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
543         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
544         if(msg.hwnd == e1)                 currentElement = 2; else\r
545         if(msg.hwnd == e2)                 currentElement = 3; else\r
546         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
547         if(msg.hwnd == mh)                currentElement = 4; else\r
548         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
549         if(msg.hwnd == hText)  currentElement = 5; else\r
550         if(msg.hwnd == hInput) currentElement = 6; else\r
551         for (i = 0; i < N_BUTTONS; i++) {\r
552             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
553         }\r
554 \r
555         // determine where to go to\r
556         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
557           do {\r
558             currentElement = (currentElement + direction) % 7;\r
559             switch(currentElement) {\r
560                 case 0:\r
561                   h = hwndMain; break; // passing this case always makes the loop exit\r
562                 case 1:\r
563                   h = buttonDesc[0].hwnd; break; // could be NULL\r
564                 case 2:\r
565                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
566                   h = e1; break;\r
567                 case 3:\r
568                   if(!EngineOutputIsUp()) continue;\r
569                   h = e2; break;\r
570                 case 4:\r
571                   if(!MoveHistoryIsUp()) continue;\r
572                   h = mh; break;\r
573 //              case 6: // input to eval graph does not seem to get here!\r
574 //                if(!EvalGraphIsUp()) continue;\r
575 //                h = evalGraphDialog; break;\r
576                 case 5:\r
577                   if(!appData.icsActive) continue;\r
578                   SAY("display");\r
579                   h = hText; break;\r
580                 case 6:\r
581                   if(!appData.icsActive) continue;\r
582                   SAY("input");\r
583                   h = hInput; break;\r
584             }\r
585           } while(h == 0);\r
586 \r
587           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
588           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
589           SetFocus(h);\r
590 \r
591           continue; // this message now has been processed\r
592         }\r
593       }\r
594 \r
595       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
596           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
597           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
598           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
599           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
600           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
601           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
602           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
603           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
604           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
605         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
606         for(i=0; i<MAX_CHAT; i++) \r
607             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
608                 done = 1; break;\r
609         }\r
610         if(done) continue; // [HGM] chat: end patch\r
611         TranslateMessage(&msg); /* Translates virtual key codes */\r
612         DispatchMessage(&msg);  /* Dispatches message to window */\r
613       }\r
614     }\r
615 \r
616 \r
617   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
618 }\r
619 \r
620 /*---------------------------------------------------------------------------*\\r
621  *\r
622  * Initialization functions\r
623  *\r
624 \*---------------------------------------------------------------------------*/\r
625 \r
626 void\r
627 SetUserLogo()\r
628 {   // update user logo if necessary\r
629     static char oldUserName[MSG_SIZ], *curName;\r
630 \r
631     if(appData.autoLogo) {\r
632           curName = UserName();\r
633           if(strcmp(curName, oldUserName)) {\r
634                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
635                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
636                 strcpy(oldUserName, curName);\r
637           }\r
638     }\r
639 }\r
640 \r
641 BOOL\r
642 InitApplication(HINSTANCE hInstance)\r
643 {\r
644   WNDCLASS wc;\r
645 \r
646   /* Fill in window class structure with parameters that describe the */\r
647   /* main window. */\r
648 \r
649   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
650   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
651   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
652   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
653   wc.hInstance     = hInstance;         /* Owner of this class */\r
654   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
655   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
656   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
657   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
658   wc.lpszClassName = szAppName;                 /* Name to register as */\r
659 \r
660   /* Register the window class and return success/failure code. */\r
661   if (!RegisterClass(&wc)) return FALSE;\r
662 \r
663   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
664   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
665   wc.cbClsExtra    = 0;\r
666   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
667   wc.hInstance     = hInstance;\r
668   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
669   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
670   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
671   wc.lpszMenuName  = NULL;\r
672   wc.lpszClassName = szConsoleName;\r
673 \r
674   if (!RegisterClass(&wc)) return FALSE;\r
675   return TRUE;\r
676 }\r
677 \r
678 \r
679 /* Set by InitInstance, used by EnsureOnScreen */\r
680 int screenHeight, screenWidth;\r
681 \r
682 void\r
683 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
684 {\r
685 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
686   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
687   if (*x > screenWidth - 32) *x = 0;\r
688   if (*y > screenHeight - 32) *y = 0;\r
689   if (*x < minX) *x = minX;\r
690   if (*y < minY) *y = minY;\r
691 }\r
692 \r
693 BOOL\r
694 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
695 {\r
696   HWND hwnd; /* Main window handle. */\r
697   int ibs;\r
698   WINDOWPLACEMENT wp;\r
699   char *filepart;\r
700 \r
701   hInst = hInstance;    /* Store instance handle in our global variable */\r
702   programName = szAppName;\r
703 \r
704   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
705     *filepart = NULLCHAR;\r
706   } else {\r
707     GetCurrentDirectory(MSG_SIZ, installDir);\r
708   }\r
709   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
710   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
711   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
712   /* xboard, and older WinBoards, controlled the move sound with the\r
713      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
714      always turn the option on (so that the backend will call us),\r
715      then let the user turn the sound off by setting it to silence if\r
716      desired.  To accommodate old winboard.ini files saved by old\r
717      versions of WinBoard, we also turn off the sound if the option\r
718      was initially set to false. [HGM] taken out of InitAppData */\r
719   if (!appData.ringBellAfterMoves) {\r
720     sounds[(int)SoundMove].name = strdup("");\r
721     appData.ringBellAfterMoves = TRUE;\r
722   }\r
723   if (appData.debugMode) {\r
724     debugFP = fopen(appData.nameOfDebugFile, "w");\r
725     setbuf(debugFP, NULL);\r
726   }\r
727 \r
728   InitBackEnd1();\r
729 \r
730 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
731 //  InitEngineUCI( installDir, &second );\r
732 \r
733   /* Create a main window for this application instance. */\r
734   hwnd = CreateWindow(szAppName, szTitle,\r
735                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
736                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
737                       NULL, NULL, hInstance, NULL);\r
738   hwndMain = hwnd;\r
739 \r
740   /* If window could not be created, return "failure" */\r
741   if (!hwnd) {\r
742     return (FALSE);\r
743   }\r
744 \r
745   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
746   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
747       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
748 \r
749       if (first.programLogo == NULL && appData.debugMode) {\r
750           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
751       }\r
752   } else if(appData.autoLogo) {\r
753       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
754         char buf[MSG_SIZ];\r
755         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
756         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
757       }\r
758   }\r
759 \r
760   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
761       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
762 \r
763       if (second.programLogo == NULL && appData.debugMode) {\r
764           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
765       }\r
766   } else if(appData.autoLogo) {\r
767       char buf[MSG_SIZ];\r
768       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
769         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
770         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771       } else\r
772       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
773         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
774         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
775       }\r
776   }\r
777 \r
778   SetUserLogo();\r
779 \r
780   iconWhite = LoadIcon(hInstance, "icon_white");\r
781   iconBlack = LoadIcon(hInstance, "icon_black");\r
782   iconCurrent = iconWhite;\r
783   InitDrawingColors();\r
784   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
785   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
786   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
787     /* Compute window size for each board size, and use the largest\r
788        size that fits on this screen as the default. */\r
789     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
790     if (boardSize == (BoardSize)-1 &&\r
791         winH <= screenHeight\r
792            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
793         && winW <= screenWidth) {\r
794       boardSize = (BoardSize)ibs;\r
795     }\r
796   }\r
797 \r
798   InitDrawingSizes(boardSize, 0);\r
799   InitMenuChecks();\r
800   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
801 \r
802   /* [AS] Load textures if specified */\r
803   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
804   \r
805   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
806       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
807       liteBackTextureMode = appData.liteBackTextureMode;\r
808 \r
809       if (liteBackTexture == NULL && appData.debugMode) {\r
810           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
811       }\r
812   }\r
813   \r
814   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
815       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
816       darkBackTextureMode = appData.darkBackTextureMode;\r
817 \r
818       if (darkBackTexture == NULL && appData.debugMode) {\r
819           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
820       }\r
821   }\r
822 \r
823   mysrandom( (unsigned) time(NULL) );\r
824 \r
825   /* [AS] Restore layout */\r
826   if( wpMoveHistory.visible ) {\r
827       MoveHistoryPopUp();\r
828   }\r
829 \r
830   if( wpEvalGraph.visible ) {\r
831       EvalGraphPopUp();\r
832   }\r
833 \r
834   if( wpEngineOutput.visible ) {\r
835       EngineOutputPopUp();\r
836   }\r
837 \r
838   InitBackEnd2();\r
839 \r
840   /* Make the window visible; update its client area; and return "success" */\r
841   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
842   wp.length = sizeof(WINDOWPLACEMENT);\r
843   wp.flags = 0;\r
844   wp.showCmd = nCmdShow;\r
845   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
846   wp.rcNormalPosition.left = wpMain.x;\r
847   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
848   wp.rcNormalPosition.top = wpMain.y;\r
849   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
850   SetWindowPlacement(hwndMain, &wp);\r
851 \r
852   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
853                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
854 \r
855   if (hwndConsole) {\r
856 #if AOT_CONSOLE\r
857     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
858                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
859 #endif\r
860     ShowWindow(hwndConsole, nCmdShow);\r
861   }\r
862   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
863   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
864 \r
865   return TRUE;\r
866 \r
867 }\r
868 \r
869 VOID\r
870 InitMenuChecks()\r
871 {\r
872   HMENU hmenu = GetMenu(hwndMain);\r
873 \r
874   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
875                         MF_BYCOMMAND|((appData.icsActive &&\r
876                                        *appData.icsCommPort != NULLCHAR) ?\r
877                                       MF_ENABLED : MF_GRAYED));\r
878   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
879                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
880                                      MF_CHECKED : MF_UNCHECKED));\r
881 }\r
882 \r
883 //---------------------------------------------------------------------------------------------------------\r
884 \r
885 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
886 #define XBOARD FALSE\r
887 \r
888 #define OPTCHAR "/"\r
889 #define SEPCHAR "="\r
890 \r
891 #include "args.h"\r
892 \r
893 // front-end part of option handling\r
894 \r
895 VOID\r
896 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
897 {\r
898   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
899   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
900   DeleteDC(hdc);\r
901   lf->lfWidth = 0;\r
902   lf->lfEscapement = 0;\r
903   lf->lfOrientation = 0;\r
904   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
905   lf->lfItalic = mfp->italic;\r
906   lf->lfUnderline = mfp->underline;\r
907   lf->lfStrikeOut = mfp->strikeout;\r
908   lf->lfCharSet = mfp->charset;\r
909   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
910   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
911   lf->lfQuality = DEFAULT_QUALITY;\r
912   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
913   strcpy(lf->lfFaceName, mfp->faceName);\r
914 }\r
915 \r
916 void\r
917 CreateFontInMF(MyFont *mf)\r
918\r
919   LFfromMFP(&mf->lf, &mf->mfp);\r
920   if (mf->hf) DeleteObject(mf->hf);\r
921   mf->hf = CreateFontIndirect(&mf->lf);\r
922 }\r
923 \r
924 // [HGM] This platform-dependent table provides the location for storing the color info\r
925 void *\r
926 colorVariable[] = {\r
927   &whitePieceColor, \r
928   &blackPieceColor, \r
929   &lightSquareColor,\r
930   &darkSquareColor, \r
931   &highlightSquareColor,\r
932   &premoveHighlightColor,\r
933   NULL,\r
934   &consoleBackgroundColor,\r
935   &appData.fontForeColorWhite,\r
936   &appData.fontBackColorWhite,\r
937   &appData.fontForeColorBlack,\r
938   &appData.fontBackColorBlack,\r
939   &appData.evalHistColorWhite,\r
940   &appData.evalHistColorBlack,\r
941   &appData.highlightArrowColor,\r
942 };\r
943 \r
944 /* Command line font name parser.  NULL name means do nothing.\r
945    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
946    For backward compatibility, syntax without the colon is also\r
947    accepted, but font names with digits in them won't work in that case.\r
948 */\r
949 VOID\r
950 ParseFontName(char *name, MyFontParams *mfp)\r
951 {\r
952   char *p, *q;\r
953   if (name == NULL) return;\r
954   p = name;\r
955   q = strchr(p, ':');\r
956   if (q) {\r
957     if (q - p >= sizeof(mfp->faceName))\r
958       ExitArgError("Font name too long:", name);\r
959     memcpy(mfp->faceName, p, q - p);\r
960     mfp->faceName[q - p] = NULLCHAR;\r
961     p = q + 1;\r
962   } else {\r
963     q = mfp->faceName;\r
964     while (*p && !isdigit(*p)) {\r
965       *q++ = *p++;\r
966       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
967         ExitArgError("Font name too long:", name);\r
968     }\r
969     while (q > mfp->faceName && q[-1] == ' ') q--;\r
970     *q = NULLCHAR;\r
971   }\r
972   if (!*p) ExitArgError("Font point size missing:", name);\r
973   mfp->pointSize = (float) atof(p);\r
974   mfp->bold = (strchr(p, 'b') != NULL);\r
975   mfp->italic = (strchr(p, 'i') != NULL);\r
976   mfp->underline = (strchr(p, 'u') != NULL);\r
977   mfp->strikeout = (strchr(p, 's') != NULL);\r
978   mfp->charset = DEFAULT_CHARSET;\r
979   q = strchr(p, 'c');\r
980   if (q)\r
981     mfp->charset = (BYTE) atoi(q+1);\r
982 }\r
983 \r
984 void\r
985 ParseFont(char *name, int number)\r
986 { // wrapper to shield back-end from 'font'\r
987   ParseFontName(name, &font[boardSize][number]->mfp);\r
988 }\r
989 \r
990 void\r
991 SetFontDefaults()\r
992 { // in WB  we have a 2D array of fonts; this initializes their description\r
993   int i, j;\r
994   /* Point font array elements to structures and\r
995      parse default font names */\r
996   for (i=0; i<NUM_FONTS; i++) {\r
997     for (j=0; j<NUM_SIZES; j++) {\r
998       font[j][i] = &fontRec[j][i];\r
999       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1000     }\r
1001   }\r
1002 }\r
1003 \r
1004 void\r
1005 CreateFonts()\r
1006 { // here we create the actual fonts from the selected descriptions\r
1007   int i, j;\r
1008   for (i=0; i<NUM_FONTS; i++) {\r
1009     for (j=0; j<NUM_SIZES; j++) {\r
1010       CreateFontInMF(font[j][i]);\r
1011     }\r
1012   }\r
1013 }\r
1014 /* Color name parser.\r
1015    X version accepts X color names, but this one\r
1016    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1017 COLORREF\r
1018 ParseColorName(char *name)\r
1019 {\r
1020   int red, green, blue, count;\r
1021   char buf[MSG_SIZ];\r
1022 \r
1023   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1024   if (count != 3) {\r
1025     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1026       &red, &green, &blue);\r
1027   }\r
1028   if (count != 3) {\r
1029     sprintf(buf, "Can't parse color name %s", name);\r
1030     DisplayError(buf, 0);\r
1031     return RGB(0, 0, 0);\r
1032   }\r
1033   return PALETTERGB(red, green, blue);\r
1034 }\r
1035 \r
1036 void\r
1037 ParseColor(int n, char *name)\r
1038 { // for WinBoard the color is an int, which needs to be derived from the string\r
1039   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1040 }\r
1041 \r
1042 void\r
1043 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1044 {\r
1045   char *e = argValue;\r
1046   int eff = 0;\r
1047 \r
1048   while (*e) {\r
1049     if (*e == 'b')      eff |= CFE_BOLD;\r
1050     else if (*e == 'i') eff |= CFE_ITALIC;\r
1051     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1052     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1053     else if (*e == '#' || isdigit(*e)) break;\r
1054     e++;\r
1055   }\r
1056   *effects = eff;\r
1057   *color   = ParseColorName(e);\r
1058 }\r
1059 \r
1060 void\r
1061 ParseTextAttribs(ColorClass cc, char *s)\r
1062 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1063     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1064     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1065 }\r
1066 \r
1067 void\r
1068 ParseBoardSize(void *addr, char *name)\r
1069 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1070   BoardSize bs = SizeTiny;\r
1071   while (sizeInfo[bs].name != NULL) {\r
1072     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1073         *(BoardSize *)addr = bs;\r
1074         return;\r
1075     }\r
1076     bs++;\r
1077   }\r
1078   ExitArgError("Unrecognized board size value", name);\r
1079 }\r
1080 \r
1081 void\r
1082 LoadAllSounds()\r
1083 { // [HGM] import name from appData first\r
1084   ColorClass cc;\r
1085   SoundClass sc;\r
1086   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1087     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1088     textAttribs[cc].sound.data = NULL;\r
1089     MyLoadSound(&textAttribs[cc].sound);\r
1090   }\r
1091   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1092     textAttribs[cc].sound.name = strdup("");\r
1093     textAttribs[cc].sound.data = NULL;\r
1094   }\r
1095   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1096     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1097     sounds[sc].data = NULL;\r
1098     MyLoadSound(&sounds[sc]);\r
1099   }\r
1100 }\r
1101 \r
1102 void\r
1103 SetCommPortDefaults()\r
1104 {\r
1105    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1106   dcb.DCBlength = sizeof(DCB);\r
1107   dcb.BaudRate = 9600;\r
1108   dcb.fBinary = TRUE;\r
1109   dcb.fParity = FALSE;\r
1110   dcb.fOutxCtsFlow = FALSE;\r
1111   dcb.fOutxDsrFlow = FALSE;\r
1112   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1113   dcb.fDsrSensitivity = FALSE;\r
1114   dcb.fTXContinueOnXoff = TRUE;\r
1115   dcb.fOutX = FALSE;\r
1116   dcb.fInX = FALSE;\r
1117   dcb.fNull = FALSE;\r
1118   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1119   dcb.fAbortOnError = FALSE;\r
1120   dcb.ByteSize = 7;\r
1121   dcb.Parity = SPACEPARITY;\r
1122   dcb.StopBits = ONESTOPBIT;\r
1123 }\r
1124 \r
1125 // [HGM] args: these three cases taken out to stay in front-end\r
1126 void\r
1127 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1128 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1129         // while the curent board size determines the element. This system should be ported to XBoard.\r
1130         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1131         int bs;\r
1132         for (bs=0; bs<NUM_SIZES; bs++) {\r
1133           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1134           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1135           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1136             ad->argName, mfp->faceName, mfp->pointSize,\r
1137             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1138             mfp->bold ? "b" : "",\r
1139             mfp->italic ? "i" : "",\r
1140             mfp->underline ? "u" : "",\r
1141             mfp->strikeout ? "s" : "",\r
1142             (int)mfp->charset);\r
1143         }\r
1144       }\r
1145 \r
1146 void\r
1147 ExportSounds()\r
1148 { // [HGM] copy the names from the internal WB variables to appData\r
1149   ColorClass cc;\r
1150   SoundClass sc;\r
1151   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1152     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1153   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1154     (&appData.soundMove)[sc] = sounds[sc].name;\r
1155 }\r
1156 \r
1157 void\r
1158 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1159 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1160         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1161         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1162           (ta->effects & CFE_BOLD) ? "b" : "",\r
1163           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1164           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1165           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1166           (ta->effects) ? " " : "",\r
1167           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1168       }\r
1169 \r
1170 void\r
1171 SaveColor(FILE *f, ArgDescriptor *ad)\r
1172 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1173         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1174         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1175           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1176 }\r
1177 \r
1178 void\r
1179 SaveBoardSize(FILE *f, char *name, void *addr)\r
1180 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1181   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1182 }\r
1183 \r
1184 void\r
1185 ParseCommPortSettings(char *s)\r
1186 { // wrapper to keep dcb from back-end\r
1187   ParseCommSettings(s, &dcb);\r
1188 }\r
1189 \r
1190 void\r
1191 GetWindowCoords()\r
1192 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1193   GetActualPlacement(hwndMain, &wpMain);\r
1194   GetActualPlacement(hwndConsole, &wpConsole);\r
1195   GetActualPlacement(commentDialog, &wpComment);\r
1196   GetActualPlacement(editTagsDialog, &wpTags);\r
1197   GetActualPlacement(gameListDialog, &wpGameList);\r
1198   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1199   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1200   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1201 }\r
1202 \r
1203 void\r
1204 PrintCommPortSettings(FILE *f, char *name)\r
1205 { // wrapper to shield back-end from DCB\r
1206       PrintCommSettings(f, name, &dcb);\r
1207 }\r
1208 \r
1209 int\r
1210 MySearchPath(char *installDir, char *name, char *fullname)\r
1211 {\r
1212   char *dummy;\r
1213   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1214 }\r
1215 \r
1216 int\r
1217 MyGetFullPathName(char *name, char *fullname)\r
1218 {\r
1219   char *dummy;\r
1220   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1221 }\r
1222 \r
1223 int\r
1224 MainWindowUp()\r
1225 { // [HGM] args: allows testing if main window is realized from back-end\r
1226   return hwndMain != NULL;\r
1227 }\r
1228 \r
1229 void\r
1230 PopUpStartupDialog()\r
1231 {\r
1232     FARPROC lpProc;\r
1233     \r
1234     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1235     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1236     FreeProcInstance(lpProc);\r
1237 }\r
1238 \r
1239 /*---------------------------------------------------------------------------*\\r
1240  *\r
1241  * GDI board drawing routines\r
1242  *\r
1243 \*---------------------------------------------------------------------------*/\r
1244 \r
1245 /* [AS] Draw square using background texture */\r
1246 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1247 {\r
1248     XFORM   x;\r
1249 \r
1250     if( mode == 0 ) {\r
1251         return; /* Should never happen! */\r
1252     }\r
1253 \r
1254     SetGraphicsMode( dst, GM_ADVANCED );\r
1255 \r
1256     switch( mode ) {\r
1257     case 1:\r
1258         /* Identity */\r
1259         break;\r
1260     case 2:\r
1261         /* X reflection */\r
1262         x.eM11 = -1.0;\r
1263         x.eM12 = 0;\r
1264         x.eM21 = 0;\r
1265         x.eM22 = 1.0;\r
1266         x.eDx = (FLOAT) dw + dx - 1;\r
1267         x.eDy = 0;\r
1268         dx = 0;\r
1269         SetWorldTransform( dst, &x );\r
1270         break;\r
1271     case 3:\r
1272         /* Y reflection */\r
1273         x.eM11 = 1.0;\r
1274         x.eM12 = 0;\r
1275         x.eM21 = 0;\r
1276         x.eM22 = -1.0;\r
1277         x.eDx = 0;\r
1278         x.eDy = (FLOAT) dh + dy - 1;\r
1279         dy = 0;\r
1280         SetWorldTransform( dst, &x );\r
1281         break;\r
1282     case 4:\r
1283         /* X/Y flip */\r
1284         x.eM11 = 0;\r
1285         x.eM12 = 1.0;\r
1286         x.eM21 = 1.0;\r
1287         x.eM22 = 0;\r
1288         x.eDx = (FLOAT) dx;\r
1289         x.eDy = (FLOAT) dy;\r
1290         dx = 0;\r
1291         dy = 0;\r
1292         SetWorldTransform( dst, &x );\r
1293         break;\r
1294     }\r
1295 \r
1296     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1297 \r
1298     x.eM11 = 1.0;\r
1299     x.eM12 = 0;\r
1300     x.eM21 = 0;\r
1301     x.eM22 = 1.0;\r
1302     x.eDx = 0;\r
1303     x.eDy = 0;\r
1304     SetWorldTransform( dst, &x );\r
1305 \r
1306     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1307 }\r
1308 \r
1309 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1310 enum {\r
1311     PM_WP = (int) WhitePawn, \r
1312     PM_WN = (int) WhiteKnight, \r
1313     PM_WB = (int) WhiteBishop, \r
1314     PM_WR = (int) WhiteRook, \r
1315     PM_WQ = (int) WhiteQueen, \r
1316     PM_WF = (int) WhiteFerz, \r
1317     PM_WW = (int) WhiteWazir, \r
1318     PM_WE = (int) WhiteAlfil, \r
1319     PM_WM = (int) WhiteMan, \r
1320     PM_WO = (int) WhiteCannon, \r
1321     PM_WU = (int) WhiteUnicorn, \r
1322     PM_WH = (int) WhiteNightrider, \r
1323     PM_WA = (int) WhiteAngel, \r
1324     PM_WC = (int) WhiteMarshall, \r
1325     PM_WAB = (int) WhiteCardinal, \r
1326     PM_WD = (int) WhiteDragon, \r
1327     PM_WL = (int) WhiteLance, \r
1328     PM_WS = (int) WhiteCobra, \r
1329     PM_WV = (int) WhiteFalcon, \r
1330     PM_WSG = (int) WhiteSilver, \r
1331     PM_WG = (int) WhiteGrasshopper, \r
1332     PM_WK = (int) WhiteKing,\r
1333     PM_BP = (int) BlackPawn, \r
1334     PM_BN = (int) BlackKnight, \r
1335     PM_BB = (int) BlackBishop, \r
1336     PM_BR = (int) BlackRook, \r
1337     PM_BQ = (int) BlackQueen, \r
1338     PM_BF = (int) BlackFerz, \r
1339     PM_BW = (int) BlackWazir, \r
1340     PM_BE = (int) BlackAlfil, \r
1341     PM_BM = (int) BlackMan,\r
1342     PM_BO = (int) BlackCannon, \r
1343     PM_BU = (int) BlackUnicorn, \r
1344     PM_BH = (int) BlackNightrider, \r
1345     PM_BA = (int) BlackAngel, \r
1346     PM_BC = (int) BlackMarshall, \r
1347     PM_BG = (int) BlackGrasshopper, \r
1348     PM_BAB = (int) BlackCardinal,\r
1349     PM_BD = (int) BlackDragon,\r
1350     PM_BL = (int) BlackLance,\r
1351     PM_BS = (int) BlackCobra,\r
1352     PM_BV = (int) BlackFalcon,\r
1353     PM_BSG = (int) BlackSilver,\r
1354     PM_BK = (int) BlackKing\r
1355 };\r
1356 \r
1357 static HFONT hPieceFont = NULL;\r
1358 static HBITMAP hPieceMask[(int) EmptySquare];\r
1359 static HBITMAP hPieceFace[(int) EmptySquare];\r
1360 static int fontBitmapSquareSize = 0;\r
1361 static char pieceToFontChar[(int) EmptySquare] =\r
1362                               { 'p', 'n', 'b', 'r', 'q', \r
1363                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1364                       'k', 'o', 'm', 'v', 't', 'w', \r
1365                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1366                                                               'l' };\r
1367 \r
1368 extern BOOL SetCharTable( char *table, const char * map );\r
1369 /* [HGM] moved to backend.c */\r
1370 \r
1371 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1372 {\r
1373     HBRUSH hbrush;\r
1374     BYTE r1 = GetRValue( color );\r
1375     BYTE g1 = GetGValue( color );\r
1376     BYTE b1 = GetBValue( color );\r
1377     BYTE r2 = r1 / 2;\r
1378     BYTE g2 = g1 / 2;\r
1379     BYTE b2 = b1 / 2;\r
1380     RECT rc;\r
1381 \r
1382     /* Create a uniform background first */\r
1383     hbrush = CreateSolidBrush( color );\r
1384     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1385     FillRect( hdc, &rc, hbrush );\r
1386     DeleteObject( hbrush );\r
1387     \r
1388     if( mode == 1 ) {\r
1389         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1390         int steps = squareSize / 2;\r
1391         int i;\r
1392 \r
1393         for( i=0; i<steps; i++ ) {\r
1394             BYTE r = r1 - (r1-r2) * i / steps;\r
1395             BYTE g = g1 - (g1-g2) * i / steps;\r
1396             BYTE b = b1 - (b1-b2) * i / steps;\r
1397 \r
1398             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1399             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1400             FillRect( hdc, &rc, hbrush );\r
1401             DeleteObject(hbrush);\r
1402         }\r
1403     }\r
1404     else if( mode == 2 ) {\r
1405         /* Diagonal gradient, good more or less for every piece */\r
1406         POINT triangle[3];\r
1407         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1408         HBRUSH hbrush_old;\r
1409         int steps = squareSize;\r
1410         int i;\r
1411 \r
1412         triangle[0].x = squareSize - steps;\r
1413         triangle[0].y = squareSize;\r
1414         triangle[1].x = squareSize;\r
1415         triangle[1].y = squareSize;\r
1416         triangle[2].x = squareSize;\r
1417         triangle[2].y = squareSize - steps;\r
1418 \r
1419         for( i=0; i<steps; i++ ) {\r
1420             BYTE r = r1 - (r1-r2) * i / steps;\r
1421             BYTE g = g1 - (g1-g2) * i / steps;\r
1422             BYTE b = b1 - (b1-b2) * i / steps;\r
1423 \r
1424             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1425             hbrush_old = SelectObject( hdc, hbrush );\r
1426             Polygon( hdc, triangle, 3 );\r
1427             SelectObject( hdc, hbrush_old );\r
1428             DeleteObject(hbrush);\r
1429             triangle[0].x++;\r
1430             triangle[2].y++;\r
1431         }\r
1432 \r
1433         SelectObject( hdc, hpen );\r
1434     }\r
1435 }\r
1436 \r
1437 /*\r
1438     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1439     seems to work ok. The main problem here is to find the "inside" of a chess\r
1440     piece: follow the steps as explained below.\r
1441 */\r
1442 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1443 {\r
1444     HBITMAP hbm;\r
1445     HBITMAP hbm_old;\r
1446     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1447     RECT rc;\r
1448     SIZE sz;\r
1449     POINT pt;\r
1450     int backColor = whitePieceColor; \r
1451     int foreColor = blackPieceColor;\r
1452     \r
1453     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1454         backColor = appData.fontBackColorWhite;\r
1455         foreColor = appData.fontForeColorWhite;\r
1456     }\r
1457     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1458         backColor = appData.fontBackColorBlack;\r
1459         foreColor = appData.fontForeColorBlack;\r
1460     }\r
1461 \r
1462     /* Mask */\r
1463     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1464 \r
1465     hbm_old = SelectObject( hdc, hbm );\r
1466 \r
1467     rc.left = 0;\r
1468     rc.top = 0;\r
1469     rc.right = squareSize;\r
1470     rc.bottom = squareSize;\r
1471 \r
1472     /* Step 1: background is now black */\r
1473     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1474 \r
1475     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1476 \r
1477     pt.x = (squareSize - sz.cx) / 2;\r
1478     pt.y = (squareSize - sz.cy) / 2;\r
1479 \r
1480     SetBkMode( hdc, TRANSPARENT );\r
1481     SetTextColor( hdc, chroma );\r
1482     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1483     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1484 \r
1485     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1486     /* Step 3: the area outside the piece is filled with white */\r
1487 //    FloodFill( hdc, 0, 0, chroma );\r
1488     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1489     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1490     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1491     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1492     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1493     /* \r
1494         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1495         but if the start point is not inside the piece we're lost!\r
1496         There should be a better way to do this... if we could create a region or path\r
1497         from the fill operation we would be fine for example.\r
1498     */\r
1499 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1500     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1501 \r
1502     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1503         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1504         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1505 \r
1506         SelectObject( dc2, bm2 );\r
1507         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1508         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1509         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1510         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1511         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1512 \r
1513         DeleteDC( dc2 );\r
1514         DeleteObject( bm2 );\r
1515     }\r
1516 \r
1517     SetTextColor( hdc, 0 );\r
1518     /* \r
1519         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1520         draw the piece again in black for safety.\r
1521     */\r
1522     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1523 \r
1524     SelectObject( hdc, hbm_old );\r
1525 \r
1526     if( hPieceMask[index] != NULL ) {\r
1527         DeleteObject( hPieceMask[index] );\r
1528     }\r
1529 \r
1530     hPieceMask[index] = hbm;\r
1531 \r
1532     /* Face */\r
1533     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1534 \r
1535     SelectObject( hdc, hbm );\r
1536 \r
1537     {\r
1538         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1539         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1540         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1541 \r
1542         SelectObject( dc1, hPieceMask[index] );\r
1543         SelectObject( dc2, bm2 );\r
1544         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1545         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1546         \r
1547         /* \r
1548             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1549             the piece background and deletes (makes transparent) the rest.\r
1550             Thanks to that mask, we are free to paint the background with the greates\r
1551             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1552             We use this, to make gradients and give the pieces a "roundish" look.\r
1553         */\r
1554         SetPieceBackground( hdc, backColor, 2 );\r
1555         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1556 \r
1557         DeleteDC( dc2 );\r
1558         DeleteDC( dc1 );\r
1559         DeleteObject( bm2 );\r
1560     }\r
1561 \r
1562     SetTextColor( hdc, foreColor );\r
1563     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1564 \r
1565     SelectObject( hdc, hbm_old );\r
1566 \r
1567     if( hPieceFace[index] != NULL ) {\r
1568         DeleteObject( hPieceFace[index] );\r
1569     }\r
1570 \r
1571     hPieceFace[index] = hbm;\r
1572 }\r
1573 \r
1574 static int TranslatePieceToFontPiece( int piece )\r
1575 {\r
1576     switch( piece ) {\r
1577     case BlackPawn:\r
1578         return PM_BP;\r
1579     case BlackKnight:\r
1580         return PM_BN;\r
1581     case BlackBishop:\r
1582         return PM_BB;\r
1583     case BlackRook:\r
1584         return PM_BR;\r
1585     case BlackQueen:\r
1586         return PM_BQ;\r
1587     case BlackKing:\r
1588         return PM_BK;\r
1589     case WhitePawn:\r
1590         return PM_WP;\r
1591     case WhiteKnight:\r
1592         return PM_WN;\r
1593     case WhiteBishop:\r
1594         return PM_WB;\r
1595     case WhiteRook:\r
1596         return PM_WR;\r
1597     case WhiteQueen:\r
1598         return PM_WQ;\r
1599     case WhiteKing:\r
1600         return PM_WK;\r
1601 \r
1602     case BlackAngel:\r
1603         return PM_BA;\r
1604     case BlackMarshall:\r
1605         return PM_BC;\r
1606     case BlackFerz:\r
1607         return PM_BF;\r
1608     case BlackNightrider:\r
1609         return PM_BH;\r
1610     case BlackAlfil:\r
1611         return PM_BE;\r
1612     case BlackWazir:\r
1613         return PM_BW;\r
1614     case BlackUnicorn:\r
1615         return PM_BU;\r
1616     case BlackCannon:\r
1617         return PM_BO;\r
1618     case BlackGrasshopper:\r
1619         return PM_BG;\r
1620     case BlackMan:\r
1621         return PM_BM;\r
1622     case BlackSilver:\r
1623         return PM_BSG;\r
1624     case BlackLance:\r
1625         return PM_BL;\r
1626     case BlackFalcon:\r
1627         return PM_BV;\r
1628     case BlackCobra:\r
1629         return PM_BS;\r
1630     case BlackCardinal:\r
1631         return PM_BAB;\r
1632     case BlackDragon:\r
1633         return PM_BD;\r
1634 \r
1635     case WhiteAngel:\r
1636         return PM_WA;\r
1637     case WhiteMarshall:\r
1638         return PM_WC;\r
1639     case WhiteFerz:\r
1640         return PM_WF;\r
1641     case WhiteNightrider:\r
1642         return PM_WH;\r
1643     case WhiteAlfil:\r
1644         return PM_WE;\r
1645     case WhiteWazir:\r
1646         return PM_WW;\r
1647     case WhiteUnicorn:\r
1648         return PM_WU;\r
1649     case WhiteCannon:\r
1650         return PM_WO;\r
1651     case WhiteGrasshopper:\r
1652         return PM_WG;\r
1653     case WhiteMan:\r
1654         return PM_WM;\r
1655     case WhiteSilver:\r
1656         return PM_WSG;\r
1657     case WhiteLance:\r
1658         return PM_WL;\r
1659     case WhiteFalcon:\r
1660         return PM_WV;\r
1661     case WhiteCobra:\r
1662         return PM_WS;\r
1663     case WhiteCardinal:\r
1664         return PM_WAB;\r
1665     case WhiteDragon:\r
1666         return PM_WD;\r
1667     }\r
1668 \r
1669     return 0;\r
1670 }\r
1671 \r
1672 void CreatePiecesFromFont()\r
1673 {\r
1674     LOGFONT lf;\r
1675     HDC hdc_window = NULL;\r
1676     HDC hdc = NULL;\r
1677     HFONT hfont_old;\r
1678     int fontHeight;\r
1679     int i;\r
1680 \r
1681     if( fontBitmapSquareSize < 0 ) {\r
1682         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1683         return;\r
1684     }\r
1685 \r
1686     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1687         fontBitmapSquareSize = -1;\r
1688         return;\r
1689     }\r
1690 \r
1691     if( fontBitmapSquareSize != squareSize ) {\r
1692         hdc_window = GetDC( hwndMain );\r
1693         hdc = CreateCompatibleDC( hdc_window );\r
1694 \r
1695         if( hPieceFont != NULL ) {\r
1696             DeleteObject( hPieceFont );\r
1697         }\r
1698         else {\r
1699             for( i=0; i<=(int)BlackKing; i++ ) {\r
1700                 hPieceMask[i] = NULL;\r
1701                 hPieceFace[i] = NULL;\r
1702             }\r
1703         }\r
1704 \r
1705         fontHeight = 75;\r
1706 \r
1707         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1708             fontHeight = appData.fontPieceSize;\r
1709         }\r
1710 \r
1711         fontHeight = (fontHeight * squareSize) / 100;\r
1712 \r
1713         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1714         lf.lfWidth = 0;\r
1715         lf.lfEscapement = 0;\r
1716         lf.lfOrientation = 0;\r
1717         lf.lfWeight = FW_NORMAL;\r
1718         lf.lfItalic = 0;\r
1719         lf.lfUnderline = 0;\r
1720         lf.lfStrikeOut = 0;\r
1721         lf.lfCharSet = DEFAULT_CHARSET;\r
1722         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1723         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1724         lf.lfQuality = PROOF_QUALITY;\r
1725         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
1726         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
1727         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
1728 \r
1729         hPieceFont = CreateFontIndirect( &lf );\r
1730 \r
1731         if( hPieceFont == NULL ) {\r
1732             fontBitmapSquareSize = -2;\r
1733         }\r
1734         else {\r
1735             /* Setup font-to-piece character table */\r
1736             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
1737                 /* No (or wrong) global settings, try to detect the font */\r
1738                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
1739                     /* Alpha */\r
1740                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
1741                 }\r
1742                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
1743                     /* DiagramTT* family */\r
1744                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
1745                 }\r
1746                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
1747                     /* Fairy symbols */\r
1748                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
1749                 }\r
1750                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
1751                     /* Good Companion (Some characters get warped as literal :-( */\r
1752                     char s[] = "1cmWG0??S??oYI23wgQU";\r
1753                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
1754                     SetCharTable(pieceToFontChar, s);\r
1755                 }\r
1756                 else {\r
1757                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
1758                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
1759                 }\r
1760             }\r
1761 \r
1762             /* Create bitmaps */\r
1763             hfont_old = SelectObject( hdc, hPieceFont );\r
1764             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
1765                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
1766                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
1767 \r
1768             SelectObject( hdc, hfont_old );\r
1769 \r
1770             fontBitmapSquareSize = squareSize;\r
1771         }\r
1772     }\r
1773 \r
1774     if( hdc != NULL ) {\r
1775         DeleteDC( hdc );\r
1776     }\r
1777 \r
1778     if( hdc_window != NULL ) {\r
1779         ReleaseDC( hwndMain, hdc_window );\r
1780     }\r
1781 }\r
1782 \r
1783 HBITMAP\r
1784 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1785 {\r
1786   char name[128];\r
1787 \r
1788   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1789   if (gameInfo.event &&\r
1790       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1791       strcmp(name, "k80s") == 0) {\r
1792     strcpy(name, "tim");\r
1793   }\r
1794   return LoadBitmap(hinst, name);\r
1795 }\r
1796 \r
1797 \r
1798 /* Insert a color into the program's logical palette\r
1799    structure.  This code assumes the given color is\r
1800    the result of the RGB or PALETTERGB macro, and it\r
1801    knows how those macros work (which is documented).\r
1802 */\r
1803 VOID\r
1804 InsertInPalette(COLORREF color)\r
1805 {\r
1806   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1807 \r
1808   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1809     DisplayFatalError("Too many colors", 0, 1);\r
1810     pLogPal->palNumEntries--;\r
1811     return;\r
1812   }\r
1813 \r
1814   pe->peFlags = (char) 0;\r
1815   pe->peRed = (char) (0xFF & color);\r
1816   pe->peGreen = (char) (0xFF & (color >> 8));\r
1817   pe->peBlue = (char) (0xFF & (color >> 16));\r
1818   return;\r
1819 }\r
1820 \r
1821 \r
1822 VOID\r
1823 InitDrawingColors()\r
1824 {\r
1825   if (pLogPal == NULL) {\r
1826     /* Allocate enough memory for a logical palette with\r
1827      * PALETTESIZE entries and set the size and version fields\r
1828      * of the logical palette structure.\r
1829      */\r
1830     pLogPal = (NPLOGPALETTE)\r
1831       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
1832                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
1833     pLogPal->palVersion    = 0x300;\r
1834   }\r
1835   pLogPal->palNumEntries = 0;\r
1836 \r
1837   InsertInPalette(lightSquareColor);\r
1838   InsertInPalette(darkSquareColor);\r
1839   InsertInPalette(whitePieceColor);\r
1840   InsertInPalette(blackPieceColor);\r
1841   InsertInPalette(highlightSquareColor);\r
1842   InsertInPalette(premoveHighlightColor);\r
1843 \r
1844   /*  create a logical color palette according the information\r
1845    *  in the LOGPALETTE structure.\r
1846    */\r
1847   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
1848 \r
1849   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
1850   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
1851   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
1852   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
1853   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
1854   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
1855   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
1856   /* [AS] Force rendering of the font-based pieces */\r
1857   if( fontBitmapSquareSize > 0 ) {\r
1858     fontBitmapSquareSize = 0;\r
1859   }\r
1860 }\r
1861 \r
1862 \r
1863 int\r
1864 BoardWidth(int boardSize, int n)\r
1865 { /* [HGM] argument n added to allow different width and height */\r
1866   int lineGap = sizeInfo[boardSize].lineGap;\r
1867 \r
1868   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1869       lineGap = appData.overrideLineGap;\r
1870   }\r
1871 \r
1872   return (n + 1) * lineGap +\r
1873           n * sizeInfo[boardSize].squareSize;\r
1874 }\r
1875 \r
1876 /* Respond to board resize by dragging edge */\r
1877 VOID\r
1878 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
1879 {\r
1880   BoardSize newSize = NUM_SIZES - 1;\r
1881   static int recurse = 0;\r
1882   if (IsIconic(hwndMain)) return;\r
1883   if (recurse > 0) return;\r
1884   recurse++;\r
1885   while (newSize > 0) {\r
1886         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
1887         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
1888            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
1889     newSize--;\r
1890   } \r
1891   boardSize = newSize;\r
1892   InitDrawingSizes(boardSize, flags);\r
1893   recurse--;\r
1894 }\r
1895 \r
1896 \r
1897 \r
1898 VOID\r
1899 InitDrawingSizes(BoardSize boardSize, int flags)\r
1900 {\r
1901   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
1902   ChessSquare piece;\r
1903   static int oldBoardSize = -1, oldTinyLayout = 0;\r
1904   HDC hdc;\r
1905   SIZE clockSize, messageSize;\r
1906   HFONT oldFont;\r
1907   char buf[MSG_SIZ];\r
1908   char *str;\r
1909   HMENU hmenu = GetMenu(hwndMain);\r
1910   RECT crect, wrect, oldRect;\r
1911   int offby;\r
1912   LOGBRUSH logbrush;\r
1913 \r
1914   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
1915   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
1916 \r
1917   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
1918   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
1919 \r
1920   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
1921   oldRect.top = wpMain.y;\r
1922   oldRect.right = wpMain.x + wpMain.width;\r
1923   oldRect.bottom = wpMain.y + wpMain.height;\r
1924 \r
1925   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
1926   smallLayout = sizeInfo[boardSize].smallLayout;\r
1927   squareSize = sizeInfo[boardSize].squareSize;\r
1928   lineGap = sizeInfo[boardSize].lineGap;\r
1929   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
1930 \r
1931   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1932       lineGap = appData.overrideLineGap;\r
1933   }\r
1934 \r
1935   if (tinyLayout != oldTinyLayout) {\r
1936     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
1937     if (tinyLayout) {\r
1938       style &= ~WS_SYSMENU;\r
1939       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
1940                  "&Minimize\tCtrl+F4");\r
1941     } else {\r
1942       style |= WS_SYSMENU;\r
1943       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
1944     }\r
1945     SetWindowLong(hwndMain, GWL_STYLE, style);\r
1946 \r
1947     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
1948       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
1949         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
1950     }\r
1951     DrawMenuBar(hwndMain);\r
1952   }\r
1953 \r
1954   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
1955   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
1956 \r
1957   /* Get text area sizes */\r
1958   hdc = GetDC(hwndMain);\r
1959   if (appData.clockMode) {\r
1960     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
1961   } else {\r
1962     sprintf(buf, "White");\r
1963   }\r
1964   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
1965   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
1966   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
1967   str = "We only care about the height here";\r
1968   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
1969   SelectObject(hdc, oldFont);\r
1970   ReleaseDC(hwndMain, hdc);\r
1971 \r
1972   /* Compute where everything goes */\r
1973   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
1974         /* [HGM] logo: if either logo is on, reserve space for it */\r
1975         logoHeight =  2*clockSize.cy;\r
1976         leftLogoRect.left   = OUTER_MARGIN;\r
1977         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
1978         leftLogoRect.top    = OUTER_MARGIN;\r
1979         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1980 \r
1981         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
1982         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
1983         rightLogoRect.top    = OUTER_MARGIN;\r
1984         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1985 \r
1986 \r
1987     whiteRect.left = leftLogoRect.right;\r
1988     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
1989     whiteRect.top = OUTER_MARGIN;\r
1990     whiteRect.bottom = whiteRect.top + logoHeight;\r
1991 \r
1992     blackRect.right = rightLogoRect.left;\r
1993     blackRect.left = whiteRect.right + INNER_MARGIN;\r
1994     blackRect.top = whiteRect.top;\r
1995     blackRect.bottom = whiteRect.bottom;\r
1996   } else {\r
1997     whiteRect.left = OUTER_MARGIN;\r
1998     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
1999     whiteRect.top = OUTER_MARGIN;\r
2000     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2001 \r
2002     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2003     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2004     blackRect.top = whiteRect.top;\r
2005     blackRect.bottom = whiteRect.bottom;\r
2006   }\r
2007 \r
2008   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2009   if (appData.showButtonBar) {\r
2010     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2011       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2012   } else {\r
2013     messageRect.right = OUTER_MARGIN + boardWidth;\r
2014   }\r
2015   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2016   messageRect.bottom = messageRect.top + messageSize.cy;\r
2017 \r
2018   boardRect.left = OUTER_MARGIN;\r
2019   boardRect.right = boardRect.left + boardWidth;\r
2020   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2021   boardRect.bottom = boardRect.top + boardHeight;\r
2022 \r
2023   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2024   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2025   oldBoardSize = boardSize;\r
2026   oldTinyLayout = tinyLayout;\r
2027   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2028   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2029     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2030   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2031   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2032   wpMain.height = winH; //       without disturbing window attachments\r
2033   GetWindowRect(hwndMain, &wrect);\r
2034   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2035                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2036 \r
2037   // [HGM] placement: let attached windows follow size change.\r
2038   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2039   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2040   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2041   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2042   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2043 \r
2044   /* compensate if menu bar wrapped */\r
2045   GetClientRect(hwndMain, &crect);\r
2046   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2047   wpMain.height += offby;\r
2048   switch (flags) {\r
2049   case WMSZ_TOPLEFT:\r
2050     SetWindowPos(hwndMain, NULL, \r
2051                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2052                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2053     break;\r
2054 \r
2055   case WMSZ_TOPRIGHT:\r
2056   case WMSZ_TOP:\r
2057     SetWindowPos(hwndMain, NULL, \r
2058                  wrect.left, wrect.bottom - wpMain.height, \r
2059                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2060     break;\r
2061 \r
2062   case WMSZ_BOTTOMLEFT:\r
2063   case WMSZ_LEFT:\r
2064     SetWindowPos(hwndMain, NULL, \r
2065                  wrect.right - wpMain.width, wrect.top, \r
2066                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2067     break;\r
2068 \r
2069   case WMSZ_BOTTOMRIGHT:\r
2070   case WMSZ_BOTTOM:\r
2071   case WMSZ_RIGHT:\r
2072   default:\r
2073     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2074                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2075     break;\r
2076   }\r
2077 \r
2078   hwndPause = NULL;\r
2079   for (i = 0; i < N_BUTTONS; i++) {\r
2080     if (buttonDesc[i].hwnd != NULL) {\r
2081       DestroyWindow(buttonDesc[i].hwnd);\r
2082       buttonDesc[i].hwnd = NULL;\r
2083     }\r
2084     if (appData.showButtonBar) {\r
2085       buttonDesc[i].hwnd =\r
2086         CreateWindow("BUTTON", buttonDesc[i].label,\r
2087                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2088                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2089                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2090                      (HMENU) buttonDesc[i].id,\r
2091                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2092       if (tinyLayout) {\r
2093         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2094                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2095                     MAKELPARAM(FALSE, 0));\r
2096       }\r
2097       if (buttonDesc[i].id == IDM_Pause)\r
2098         hwndPause = buttonDesc[i].hwnd;\r
2099       buttonDesc[i].wndproc = (WNDPROC)\r
2100         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2101     }\r
2102   }\r
2103   if (gridPen != NULL) DeleteObject(gridPen);\r
2104   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2105   if (premovePen != NULL) DeleteObject(premovePen);\r
2106   if (lineGap != 0) {\r
2107     logbrush.lbStyle = BS_SOLID;\r
2108     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2109     gridPen =\r
2110       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2111                    lineGap, &logbrush, 0, NULL);\r
2112     logbrush.lbColor = highlightSquareColor;\r
2113     highlightPen =\r
2114       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2115                    lineGap, &logbrush, 0, NULL);\r
2116 \r
2117     logbrush.lbColor = premoveHighlightColor; \r
2118     premovePen =\r
2119       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2120                    lineGap, &logbrush, 0, NULL);\r
2121 \r
2122     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2123     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2124       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2125       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2126         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2127       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2128         BOARD_WIDTH * (squareSize + lineGap);\r
2129       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2130     }\r
2131     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2132       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2133       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2134         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2135         lineGap / 2 + (i * (squareSize + lineGap));\r
2136       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2137         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2138       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2139     }\r
2140   }\r
2141 \r
2142   /* [HGM] Licensing requirement */\r
2143 #ifdef GOTHIC\r
2144   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2145 #endif\r
2146 #ifdef FALCON\r
2147   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2148 #endif\r
2149   GothicPopUp( "", VariantNormal);\r
2150 \r
2151 \r
2152 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2153 \r
2154   /* Load piece bitmaps for this board size */\r
2155   for (i=0; i<=2; i++) {\r
2156     for (piece = WhitePawn;\r
2157          (int) piece < (int) BlackPawn;\r
2158          piece = (ChessSquare) ((int) piece + 1)) {\r
2159       if (pieceBitmap[i][piece] != NULL)\r
2160         DeleteObject(pieceBitmap[i][piece]);\r
2161     }\r
2162   }\r
2163 \r
2164   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2165   // Orthodox Chess pieces\r
2166   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2167   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2168   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2169   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2170   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2171   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2172   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2173   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2174   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2175   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2176   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2177   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2178   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2179   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2180   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2181   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2182     // in Shogi, Hijack the unused Queen for Lance\r
2183     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2184     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2185     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2186   } else {\r
2187     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2188     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2189     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2190   }\r
2191 \r
2192   if(squareSize <= 72 && squareSize >= 33) { \r
2193     /* A & C are available in most sizes now */\r
2194     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2195       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2196       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2197       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2198       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2199       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2200       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2201       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2202       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2203       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2204       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2205       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2206       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2207     } else { // Smirf-like\r
2208       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2209       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2210       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2211     }\r
2212     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2213       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2214       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2215       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2216     } else { // WinBoard standard\r
2217       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2218       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2219       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2220     }\r
2221   }\r
2222 \r
2223 \r
2224   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2225     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2226     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2227     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2228     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2229     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2230     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2231     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2232     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2233     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2234     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2235     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2236     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2237     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2238     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2239     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2240     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2241     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2242     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2243     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2244     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2245     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2246     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2247     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2248     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2249     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2250     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2251     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2252     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2253     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2254     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2255 \r
2256     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2257       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2258       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2259       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2260       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2261       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2262       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2263       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2264       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2265       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2266       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2267       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2268       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2269     } else {\r
2270       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2271       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2272       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2273       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2274       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2275       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2276       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2277       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2278       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2279       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2280       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2281       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2282     }\r
2283 \r
2284   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2285     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2286     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2287     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2288     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2289     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2290     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2291     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2292     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2293     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2294     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2295     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2296     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2297     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2298     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2299   }\r
2300 \r
2301 \r
2302   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2303   /* special Shogi support in this size */\r
2304   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2305       for (piece = WhitePawn;\r
2306            (int) piece < (int) BlackPawn;\r
2307            piece = (ChessSquare) ((int) piece + 1)) {\r
2308         if (pieceBitmap[i][piece] != NULL)\r
2309           DeleteObject(pieceBitmap[i][piece]);\r
2310       }\r
2311     }\r
2312   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2313   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2314   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2315   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2316   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2317   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2318   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2319   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2320   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2321   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2322   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2323   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2324   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2325   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2326   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2327   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2328   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2329   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2330   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2331   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2332   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2333   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2334   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2335   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2336   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2337   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2338   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2339   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2340   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2341   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2342   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2343   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2344   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2345   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2346   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2347   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2348   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2349   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2350   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2351   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2352   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2353   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2354   minorSize = 0;\r
2355   }\r
2356 }\r
2357 \r
2358 HBITMAP\r
2359 PieceBitmap(ChessSquare p, int kind)\r
2360 {\r
2361   if ((int) p >= (int) BlackPawn)\r
2362     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2363 \r
2364   return pieceBitmap[kind][(int) p];\r
2365 }\r
2366 \r
2367 /***************************************************************/\r
2368 \r
2369 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2370 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2371 /*\r
2372 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2373 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2374 */\r
2375 \r
2376 VOID\r
2377 SquareToPos(int row, int column, int * x, int * y)\r
2378 {\r
2379   if (flipView) {\r
2380     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2381     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2382   } else {\r
2383     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2384     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2385   }\r
2386 }\r
2387 \r
2388 VOID\r
2389 DrawCoordsOnDC(HDC hdc)\r
2390 {\r
2391   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2392   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2393   char str[2] = { NULLCHAR, NULLCHAR };\r
2394   int oldMode, oldAlign, x, y, start, i;\r
2395   HFONT oldFont;\r
2396   HBRUSH oldBrush;\r
2397 \r
2398   if (!appData.showCoords)\r
2399     return;\r
2400 \r
2401   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2402 \r
2403   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2404   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2405   oldAlign = GetTextAlign(hdc);\r
2406   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2407 \r
2408   y = boardRect.top + lineGap;\r
2409   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2410 \r
2411   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2412   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2413     str[0] = files[start + i];\r
2414     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2415     y += squareSize + lineGap;\r
2416   }\r
2417 \r
2418   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2419 \r
2420   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2421   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2422     str[0] = ranks[start + i];\r
2423     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2424     x += squareSize + lineGap;\r
2425   }    \r
2426 \r
2427   SelectObject(hdc, oldBrush);\r
2428   SetBkMode(hdc, oldMode);\r
2429   SetTextAlign(hdc, oldAlign);\r
2430   SelectObject(hdc, oldFont);\r
2431 }\r
2432 \r
2433 VOID\r
2434 DrawGridOnDC(HDC hdc)\r
2435 {\r
2436   HPEN oldPen;\r
2437  \r
2438   if (lineGap != 0) {\r
2439     oldPen = SelectObject(hdc, gridPen);\r
2440     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2441     SelectObject(hdc, oldPen);\r
2442   }\r
2443 }\r
2444 \r
2445 #define HIGHLIGHT_PEN 0\r
2446 #define PREMOVE_PEN   1\r
2447 \r
2448 VOID\r
2449 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2450 {\r
2451   int x1, y1;\r
2452   HPEN oldPen, hPen;\r
2453   if (lineGap == 0) return;\r
2454   if (flipView) {\r
2455     x1 = boardRect.left +\r
2456       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2457     y1 = boardRect.top +\r
2458       lineGap/2 + y * (squareSize + lineGap);\r
2459   } else {\r
2460     x1 = boardRect.left +\r
2461       lineGap/2 + x * (squareSize + lineGap);\r
2462     y1 = boardRect.top +\r
2463       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2464   }\r
2465   hPen = pen ? premovePen : highlightPen;\r
2466   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2467   MoveToEx(hdc, x1, y1, NULL);\r
2468   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2469   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2470   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2471   LineTo(hdc, x1, y1);\r
2472   SelectObject(hdc, oldPen);\r
2473 }\r
2474 \r
2475 VOID\r
2476 DrawHighlightsOnDC(HDC hdc)\r
2477 {\r
2478   int i;\r
2479   for (i=0; i<2; i++) {\r
2480     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2481       DrawHighlightOnDC(hdc, TRUE,\r
2482                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2483                         HIGHLIGHT_PEN);\r
2484   }\r
2485   for (i=0; i<2; i++) {\r
2486     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2487         premoveHighlightInfo.sq[i].y >= 0) {\r
2488         DrawHighlightOnDC(hdc, TRUE,\r
2489                           premoveHighlightInfo.sq[i].x, \r
2490                           premoveHighlightInfo.sq[i].y,\r
2491                           PREMOVE_PEN);\r
2492     }\r
2493   }\r
2494 }\r
2495 \r
2496 /* Note: sqcolor is used only in monoMode */\r
2497 /* Note that this code is largely duplicated in woptions.c,\r
2498    function DrawSampleSquare, so that needs to be updated too */\r
2499 VOID\r
2500 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2501 {\r
2502   HBITMAP oldBitmap;\r
2503   HBRUSH oldBrush;\r
2504   int tmpSize;\r
2505 \r
2506   if (appData.blindfold) return;\r
2507 \r
2508   /* [AS] Use font-based pieces if needed */\r
2509   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
2510     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2511     CreatePiecesFromFont();\r
2512 \r
2513     if( fontBitmapSquareSize == squareSize ) {\r
2514         int index = TranslatePieceToFontPiece(piece);\r
2515 \r
2516         SelectObject( tmphdc, hPieceMask[ index ] );\r
2517 \r
2518         BitBlt( hdc,\r
2519             x, y,\r
2520             squareSize, squareSize,\r
2521             tmphdc,\r
2522             0, 0,\r
2523             SRCAND );\r
2524 \r
2525         SelectObject( tmphdc, hPieceFace[ index ] );\r
2526 \r
2527         BitBlt( hdc,\r
2528             x, y,\r
2529             squareSize, squareSize,\r
2530             tmphdc,\r
2531             0, 0,\r
2532             SRCPAINT );\r
2533 \r
2534         return;\r
2535     }\r
2536   }\r
2537 \r
2538   if (appData.monoMode) {\r
2539     SelectObject(tmphdc, PieceBitmap(piece, \r
2540       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2541     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2542            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2543   } else {\r
2544     tmpSize = squareSize;\r
2545     if(minorSize &&\r
2546         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2547          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2548       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2549       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2550       x += (squareSize - minorSize)>>1;\r
2551       y += squareSize - minorSize - 2;\r
2552       tmpSize = minorSize;\r
2553     }\r
2554     if (color || appData.allWhite ) {\r
2555       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2556       if( color )\r
2557               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2558       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2559       if(appData.upsideDown && color==flipView)\r
2560         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2561       else\r
2562         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2563       /* Use black for outline of white pieces */\r
2564       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2565       if(appData.upsideDown && color==flipView)\r
2566         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2567       else\r
2568         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2569     } else {\r
2570       /* Use square color for details of black pieces */\r
2571       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2572       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2573       if(appData.upsideDown && !flipView)\r
2574         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2575       else\r
2576         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2577     }\r
2578     SelectObject(hdc, oldBrush);\r
2579     SelectObject(tmphdc, oldBitmap);\r
2580   }\r
2581 }\r
2582 \r
2583 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2584 int GetBackTextureMode( int algo )\r
2585 {\r
2586     int result = BACK_TEXTURE_MODE_DISABLED;\r
2587 \r
2588     switch( algo ) \r
2589     {\r
2590         case BACK_TEXTURE_MODE_PLAIN:\r
2591             result = 1; /* Always use identity map */\r
2592             break;\r
2593         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2594             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2595             break;\r
2596     }\r
2597 \r
2598     return result;\r
2599 }\r
2600 \r
2601 /* \r
2602     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2603     to handle redraws cleanly (as random numbers would always be different).\r
2604 */\r
2605 VOID RebuildTextureSquareInfo()\r
2606 {\r
2607     BITMAP bi;\r
2608     int lite_w = 0;\r
2609     int lite_h = 0;\r
2610     int dark_w = 0;\r
2611     int dark_h = 0;\r
2612     int row;\r
2613     int col;\r
2614 \r
2615     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2616 \r
2617     if( liteBackTexture != NULL ) {\r
2618         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2619             lite_w = bi.bmWidth;\r
2620             lite_h = bi.bmHeight;\r
2621         }\r
2622     }\r
2623 \r
2624     if( darkBackTexture != NULL ) {\r
2625         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2626             dark_w = bi.bmWidth;\r
2627             dark_h = bi.bmHeight;\r
2628         }\r
2629     }\r
2630 \r
2631     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2632         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2633             if( (col + row) & 1 ) {\r
2634                 /* Lite square */\r
2635                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2636                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2637                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2638                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2639                 }\r
2640             }\r
2641             else {\r
2642                 /* Dark square */\r
2643                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2644                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2645                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2646                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2647                 }\r
2648             }\r
2649         }\r
2650     }\r
2651 }\r
2652 \r
2653 /* [AS] Arrow highlighting support */\r
2654 \r
2655 static int A_WIDTH = 5; /* Width of arrow body */\r
2656 \r
2657 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2658 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2659 \r
2660 static double Sqr( double x )\r
2661 {\r
2662     return x*x;\r
2663 }\r
2664 \r
2665 static int Round( double x )\r
2666 {\r
2667     return (int) (x + 0.5);\r
2668 }\r
2669 \r
2670 /* Draw an arrow between two points using current settings */\r
2671 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2672 {\r
2673     POINT arrow[7];\r
2674     double dx, dy, j, k, x, y;\r
2675 \r
2676     if( d_x == s_x ) {\r
2677         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2678 \r
2679         arrow[0].x = s_x + A_WIDTH;\r
2680         arrow[0].y = s_y;\r
2681 \r
2682         arrow[1].x = s_x + A_WIDTH;\r
2683         arrow[1].y = d_y - h;\r
2684 \r
2685         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2686         arrow[2].y = d_y - h;\r
2687 \r
2688         arrow[3].x = d_x;\r
2689         arrow[3].y = d_y;\r
2690 \r
2691         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2692         arrow[4].y = d_y - h;\r
2693 \r
2694         arrow[5].x = s_x - A_WIDTH;\r
2695         arrow[5].y = d_y - h;\r
2696 \r
2697         arrow[6].x = s_x - A_WIDTH;\r
2698         arrow[6].y = s_y;\r
2699     }\r
2700     else if( d_y == s_y ) {\r
2701         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2702 \r
2703         arrow[0].x = s_x;\r
2704         arrow[0].y = s_y + A_WIDTH;\r
2705 \r
2706         arrow[1].x = d_x - w;\r
2707         arrow[1].y = s_y + A_WIDTH;\r
2708 \r
2709         arrow[2].x = d_x - w;\r
2710         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2711 \r
2712         arrow[3].x = d_x;\r
2713         arrow[3].y = d_y;\r
2714 \r
2715         arrow[4].x = d_x - w;\r
2716         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2717 \r
2718         arrow[5].x = d_x - w;\r
2719         arrow[5].y = s_y - A_WIDTH;\r
2720 \r
2721         arrow[6].x = s_x;\r
2722         arrow[6].y = s_y - A_WIDTH;\r
2723     }\r
2724     else {\r
2725         /* [AS] Needed a lot of paper for this! :-) */\r
2726         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2727         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2728   \r
2729         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2730 \r
2731         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2732 \r
2733         x = s_x;\r
2734         y = s_y;\r
2735 \r
2736         arrow[0].x = Round(x - j);\r
2737         arrow[0].y = Round(y + j*dx);\r
2738 \r
2739         arrow[1].x = Round(x + j);\r
2740         arrow[1].y = Round(y - j*dx);\r
2741 \r
2742         if( d_x > s_x ) {\r
2743             x = (double) d_x - k;\r
2744             y = (double) d_y - k*dy;\r
2745         }\r
2746         else {\r
2747             x = (double) d_x + k;\r
2748             y = (double) d_y + k*dy;\r
2749         }\r
2750 \r
2751         arrow[2].x = Round(x + j);\r
2752         arrow[2].y = Round(y - j*dx);\r
2753 \r
2754         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2755         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2756 \r
2757         arrow[4].x = d_x;\r
2758         arrow[4].y = d_y;\r
2759 \r
2760         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2761         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2762 \r
2763         arrow[6].x = Round(x - j);\r
2764         arrow[6].y = Round(y + j*dx);\r
2765     }\r
2766 \r
2767     Polygon( hdc, arrow, 7 );\r
2768 }\r
2769 \r
2770 /* [AS] Draw an arrow between two squares */\r
2771 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2772 {\r
2773     int s_x, s_y, d_x, d_y;\r
2774     HPEN hpen;\r
2775     HPEN holdpen;\r
2776     HBRUSH hbrush;\r
2777     HBRUSH holdbrush;\r
2778     LOGBRUSH stLB;\r
2779 \r
2780     if( s_col == d_col && s_row == d_row ) {\r
2781         return;\r
2782     }\r
2783 \r
2784     /* Get source and destination points */\r
2785     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2786     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2787 \r
2788     if( d_y > s_y ) {\r
2789         d_y += squareSize / 4;\r
2790     }\r
2791     else if( d_y < s_y ) {\r
2792         d_y += 3 * squareSize / 4;\r
2793     }\r
2794     else {\r
2795         d_y += squareSize / 2;\r
2796     }\r
2797 \r
2798     if( d_x > s_x ) {\r
2799         d_x += squareSize / 4;\r
2800     }\r
2801     else if( d_x < s_x ) {\r
2802         d_x += 3 * squareSize / 4;\r
2803     }\r
2804     else {\r
2805         d_x += squareSize / 2;\r
2806     }\r
2807 \r
2808     s_x += squareSize / 2;\r
2809     s_y += squareSize / 2;\r
2810 \r
2811     /* Adjust width */\r
2812     A_WIDTH = squareSize / 14;\r
2813 \r
2814     /* Draw */\r
2815     stLB.lbStyle = BS_SOLID;\r
2816     stLB.lbColor = appData.highlightArrowColor;\r
2817     stLB.lbHatch = 0;\r
2818 \r
2819     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2820     holdpen = SelectObject( hdc, hpen );\r
2821     hbrush = CreateBrushIndirect( &stLB );\r
2822     holdbrush = SelectObject( hdc, hbrush );\r
2823 \r
2824     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2825 \r
2826     SelectObject( hdc, holdpen );\r
2827     SelectObject( hdc, holdbrush );\r
2828     DeleteObject( hpen );\r
2829     DeleteObject( hbrush );\r
2830 }\r
2831 \r
2832 BOOL HasHighlightInfo()\r
2833 {\r
2834     BOOL result = FALSE;\r
2835 \r
2836     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2837         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2838     {\r
2839         result = TRUE;\r
2840     }\r
2841 \r
2842     return result;\r
2843 }\r
2844 \r
2845 BOOL IsDrawArrowEnabled()\r
2846 {\r
2847     BOOL result = FALSE;\r
2848 \r
2849     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2850         result = TRUE;\r
2851     }\r
2852 \r
2853     return result;\r
2854 }\r
2855 \r
2856 VOID DrawArrowHighlight( HDC hdc )\r
2857 {\r
2858     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2859         DrawArrowBetweenSquares( hdc,\r
2860             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2861             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2862     }\r
2863 }\r
2864 \r
2865 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2866 {\r
2867     HRGN result = NULL;\r
2868 \r
2869     if( HasHighlightInfo() ) {\r
2870         int x1, y1, x2, y2;\r
2871         int sx, sy, dx, dy;\r
2872 \r
2873         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2874         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2875 \r
2876         sx = MIN( x1, x2 );\r
2877         sy = MIN( y1, y2 );\r
2878         dx = MAX( x1, x2 ) + squareSize;\r
2879         dy = MAX( y1, y2 ) + squareSize;\r
2880 \r
2881         result = CreateRectRgn( sx, sy, dx, dy );\r
2882     }\r
2883 \r
2884     return result;\r
2885 }\r
2886 \r
2887 /*\r
2888     Warning: this function modifies the behavior of several other functions. \r
2889     \r
2890     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2891     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2892     repaint is scattered all over the place, which is not good for features such as\r
2893     "arrow highlighting" that require a full repaint of the board.\r
2894 \r
2895     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2896     user interaction, when speed is not so important) but especially to avoid errors\r
2897     in the displayed graphics.\r
2898 \r
2899     In such patched places, I always try refer to this function so there is a single\r
2900     place to maintain knowledge.\r
2901     \r
2902     To restore the original behavior, just return FALSE unconditionally.\r
2903 */\r
2904 BOOL IsFullRepaintPreferrable()\r
2905 {\r
2906     BOOL result = FALSE;\r
2907 \r
2908     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2909         /* Arrow may appear on the board */\r
2910         result = TRUE;\r
2911     }\r
2912 \r
2913     return result;\r
2914 }\r
2915 \r
2916 /* \r
2917     This function is called by DrawPosition to know whether a full repaint must\r
2918     be forced or not.\r
2919 \r
2920     Only DrawPosition may directly call this function, which makes use of \r
2921     some state information. Other function should call DrawPosition specifying \r
2922     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2923 */\r
2924 BOOL DrawPositionNeedsFullRepaint()\r
2925 {\r
2926     BOOL result = FALSE;\r
2927 \r
2928     /* \r
2929         Probably a slightly better policy would be to trigger a full repaint\r
2930         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2931         but animation is fast enough that it's difficult to notice.\r
2932     */\r
2933     if( animInfo.piece == EmptySquare ) {\r
2934         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2935             result = TRUE;\r
2936         }\r
2937     }\r
2938 \r
2939     return result;\r
2940 }\r
2941 \r
2942 VOID\r
2943 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2944 {\r
2945   int row, column, x, y, square_color, piece_color;\r
2946   ChessSquare piece;\r
2947   HBRUSH oldBrush;\r
2948   HDC texture_hdc = NULL;\r
2949 \r
2950   /* [AS] Initialize background textures if needed */\r
2951   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2952       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2953       if( backTextureSquareSize != squareSize \r
2954        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2955           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2956           backTextureSquareSize = squareSize;\r
2957           RebuildTextureSquareInfo();\r
2958       }\r
2959 \r
2960       texture_hdc = CreateCompatibleDC( hdc );\r
2961   }\r
2962 \r
2963   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2964     for (column = 0; column < BOARD_WIDTH; column++) {\r
2965   \r
2966       SquareToPos(row, column, &x, &y);\r
2967 \r
2968       piece = board[row][column];\r
2969 \r
2970       square_color = ((column + row) % 2) == 1;\r
2971       if( gameInfo.variant == VariantXiangqi ) {\r
2972           square_color = !InPalace(row, column);\r
2973           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
2974           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
2975       }\r
2976       piece_color = (int) piece < (int) BlackPawn;\r
2977 \r
2978 \r
2979       /* [HGM] holdings file: light square or black */\r
2980       if(column == BOARD_LEFT-2) {\r
2981             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
2982                 square_color = 1;\r
2983             else {\r
2984                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
2985                 continue;\r
2986             }\r
2987       } else\r
2988       if(column == BOARD_RGHT + 1 ) {\r
2989             if( row < gameInfo.holdingsSize )\r
2990                 square_color = 1;\r
2991             else {\r
2992                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
2993                 continue;\r
2994             }\r
2995       }\r
2996       if(column == BOARD_LEFT-1 ) /* left align */\r
2997             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
2998       else if( column == BOARD_RGHT) /* right align */\r
2999             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3000       else\r
3001       if (appData.monoMode) {\r
3002         if (piece == EmptySquare) {\r
3003           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3004                  square_color ? WHITENESS : BLACKNESS);\r
3005         } else {\r
3006           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3007         }\r
3008       } \r
3009       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3010           /* [AS] Draw the square using a texture bitmap */\r
3011           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3012           int r = row, c = column; // [HGM] do not flip board in flipView\r
3013           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3014 \r
3015           DrawTile( x, y, \r
3016               squareSize, squareSize, \r
3017               hdc, \r
3018               texture_hdc,\r
3019               backTextureSquareInfo[r][c].mode,\r
3020               backTextureSquareInfo[r][c].x,\r
3021               backTextureSquareInfo[r][c].y );\r
3022 \r
3023           SelectObject( texture_hdc, hbm );\r
3024 \r
3025           if (piece != EmptySquare) {\r
3026               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3027           }\r
3028       }\r
3029       else {\r
3030         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3031 \r
3032         oldBrush = SelectObject(hdc, brush );\r
3033         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3034         SelectObject(hdc, oldBrush);\r
3035         if (piece != EmptySquare)\r
3036           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3037       }\r
3038     }\r
3039   }\r
3040 \r
3041   if( texture_hdc != NULL ) {\r
3042     DeleteDC( texture_hdc );\r
3043   }\r
3044 }\r
3045 \r
3046 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3047 void fputDW(FILE *f, int x)\r
3048 {\r
3049         fputc(x     & 255, f);\r
3050         fputc(x>>8  & 255, f);\r
3051         fputc(x>>16 & 255, f);\r
3052         fputc(x>>24 & 255, f);\r
3053 }\r
3054 \r
3055 #define MAX_CLIPS 200   /* more than enough */\r
3056 \r
3057 VOID\r
3058 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3059 {\r
3060 //  HBITMAP bufferBitmap;\r
3061   BITMAP bi;\r
3062 //  RECT Rect;\r
3063   HDC tmphdc;\r
3064   HBITMAP hbm;\r
3065   int w = 100, h = 50;\r
3066 \r
3067   if(logo == NULL) return;\r
3068 //  GetClientRect(hwndMain, &Rect);\r
3069 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3070 //                                      Rect.bottom-Rect.top+1);\r
3071   tmphdc = CreateCompatibleDC(hdc);\r
3072   hbm = SelectObject(tmphdc, logo);\r
3073   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3074             w = bi.bmWidth;\r
3075             h = bi.bmHeight;\r
3076   }\r
3077   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3078                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3079   SelectObject(tmphdc, hbm);\r
3080   DeleteDC(tmphdc);\r
3081 }\r
3082 \r
3083 VOID\r
3084 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3085 {\r
3086   static Board lastReq, lastDrawn;\r
3087   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3088   static int lastDrawnFlipView = 0;\r
3089   static int lastReqValid = 0, lastDrawnValid = 0;\r
3090   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3091   HDC tmphdc;\r
3092   HDC hdcmem;\r
3093   HBITMAP bufferBitmap;\r
3094   HBITMAP oldBitmap;\r
3095   RECT Rect;\r
3096   HRGN clips[MAX_CLIPS];\r
3097   ChessSquare dragged_piece = EmptySquare;\r
3098 \r
3099   /* I'm undecided on this - this function figures out whether a full\r
3100    * repaint is necessary on its own, so there's no real reason to have the\r
3101    * caller tell it that.  I think this can safely be set to FALSE - but\r
3102    * if we trust the callers not to request full repaints unnessesarily, then\r
3103    * we could skip some clipping work.  In other words, only request a full\r
3104    * redraw when the majority of pieces have changed positions (ie. flip, \r
3105    * gamestart and similar)  --Hawk\r
3106    */\r
3107   Boolean fullrepaint = repaint;\r
3108 \r
3109   if( DrawPositionNeedsFullRepaint() ) {\r
3110       fullrepaint = TRUE;\r
3111   }\r
3112 \r
3113   if (board == NULL) {\r
3114     if (!lastReqValid) {\r
3115       return;\r
3116     }\r
3117     board = lastReq;\r
3118   } else {\r
3119     CopyBoard(lastReq, board);\r
3120     lastReqValid = 1;\r
3121   }\r
3122 \r
3123   if (doingSizing) {\r
3124     return;\r
3125   }\r
3126 \r
3127   if (IsIconic(hwndMain)) {\r
3128     return;\r
3129   }\r
3130 \r
3131   if (hdc == NULL) {\r
3132     hdc = GetDC(hwndMain);\r
3133     if (!appData.monoMode) {\r
3134       SelectPalette(hdc, hPal, FALSE);\r
3135       RealizePalette(hdc);\r
3136     }\r
3137     releaseDC = TRUE;\r
3138   } else {\r
3139     releaseDC = FALSE;\r
3140   }\r
3141 \r
3142   /* Create some work-DCs */\r
3143   hdcmem = CreateCompatibleDC(hdc);\r
3144   tmphdc = CreateCompatibleDC(hdc);\r
3145 \r
3146   /* If dragging is in progress, we temporarely remove the piece */\r
3147   /* [HGM] or temporarily decrease count if stacked              */\r
3148   /*       !! Moved to before board compare !!                   */\r
3149   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3150     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3151     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3152             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3153         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3154     } else \r
3155     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3156             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3157         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3158     } else \r
3159         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3160   }\r
3161 \r
3162   /* Figure out which squares need updating by comparing the \r
3163    * newest board with the last drawn board and checking if\r
3164    * flipping has changed.\r
3165    */\r
3166   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3167     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3168       for (column = 0; column < BOARD_WIDTH; column++) {\r
3169         if (lastDrawn[row][column] != board[row][column]) {\r
3170           SquareToPos(row, column, &x, &y);\r
3171           clips[num_clips++] =\r
3172             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3173         }\r
3174       }\r
3175     }\r
3176     for (i=0; i<2; i++) {\r
3177       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3178           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3179         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3180             lastDrawnHighlight.sq[i].y >= 0) {\r
3181           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3182                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3183           clips[num_clips++] =\r
3184             CreateRectRgn(x - lineGap, y - lineGap, \r
3185                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3186         }\r
3187         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3188           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3189           clips[num_clips++] =\r
3190             CreateRectRgn(x - lineGap, y - lineGap, \r
3191                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3192         }\r
3193       }\r
3194     }\r
3195     for (i=0; i<2; i++) {\r
3196       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3197           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3198         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3199             lastDrawnPremove.sq[i].y >= 0) {\r
3200           SquareToPos(lastDrawnPremove.sq[i].y,\r
3201                       lastDrawnPremove.sq[i].x, &x, &y);\r
3202           clips[num_clips++] =\r
3203             CreateRectRgn(x - lineGap, y - lineGap, \r
3204                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3205         }\r
3206         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3207             premoveHighlightInfo.sq[i].y >= 0) {\r
3208           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3209                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3210           clips[num_clips++] =\r
3211             CreateRectRgn(x - lineGap, y - lineGap, \r
3212                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3213         }\r
3214       }\r
3215     }\r
3216   } else {\r
3217     fullrepaint = TRUE;\r
3218   }\r
3219 \r
3220   /* Create a buffer bitmap - this is the actual bitmap\r
3221    * being written to.  When all the work is done, we can\r
3222    * copy it to the real DC (the screen).  This avoids\r
3223    * the problems with flickering.\r
3224    */\r
3225   GetClientRect(hwndMain, &Rect);\r
3226   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3227                                         Rect.bottom-Rect.top+1);\r
3228   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3229   if (!appData.monoMode) {\r
3230     SelectPalette(hdcmem, hPal, FALSE);\r
3231   }\r
3232 \r
3233   /* Create clips for dragging */\r
3234   if (!fullrepaint) {\r
3235     if (dragInfo.from.x >= 0) {\r
3236       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3237       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3238     }\r
3239     if (dragInfo.start.x >= 0) {\r
3240       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3241       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3242     }\r
3243     if (dragInfo.pos.x >= 0) {\r
3244       x = dragInfo.pos.x - squareSize / 2;\r
3245       y = dragInfo.pos.y - squareSize / 2;\r
3246       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3247     }\r
3248     if (dragInfo.lastpos.x >= 0) {\r
3249       x = dragInfo.lastpos.x - squareSize / 2;\r
3250       y = dragInfo.lastpos.y - squareSize / 2;\r
3251       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3252     }\r
3253   }\r
3254 \r
3255   /* Are we animating a move?  \r
3256    * If so, \r
3257    *   - remove the piece from the board (temporarely)\r
3258    *   - calculate the clipping region\r
3259    */\r
3260   if (!fullrepaint) {\r
3261     if (animInfo.piece != EmptySquare) {\r
3262       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3263       x = boardRect.left + animInfo.lastpos.x;\r
3264       y = boardRect.top + animInfo.lastpos.y;\r
3265       x2 = boardRect.left + animInfo.pos.x;\r
3266       y2 = boardRect.top + animInfo.pos.y;\r
3267       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3268       /* Slight kludge.  The real problem is that after AnimateMove is\r
3269          done, the position on the screen does not match lastDrawn.\r
3270          This currently causes trouble only on e.p. captures in\r
3271          atomic, where the piece moves to an empty square and then\r
3272          explodes.  The old and new positions both had an empty square\r
3273          at the destination, but animation has drawn a piece there and\r
3274          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3275       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3276     }\r
3277   }\r
3278 \r
3279   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3280   if (num_clips == 0)\r
3281     fullrepaint = TRUE;\r
3282 \r
3283   /* Set clipping on the memory DC */\r
3284   if (!fullrepaint) {\r
3285     SelectClipRgn(hdcmem, clips[0]);\r
3286     for (x = 1; x < num_clips; x++) {\r
3287       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3288         abort();  // this should never ever happen!\r
3289     }\r
3290   }\r
3291 \r
3292   /* Do all the drawing to the memory DC */\r
3293   if(explodeInfo.radius) { // [HGM] atomic\r
3294         HBRUSH oldBrush;\r
3295         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3296         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3297         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3298         x += squareSize/2;\r
3299         y += squareSize/2;\r
3300         if(!fullrepaint) {\r
3301           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3302           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3303         }\r
3304         DrawGridOnDC(hdcmem);\r
3305         DrawHighlightsOnDC(hdcmem);\r
3306         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3307         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3308         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3309         SelectObject(hdcmem, oldBrush);\r
3310   } else {\r
3311     DrawGridOnDC(hdcmem);\r
3312     DrawHighlightsOnDC(hdcmem);\r
3313     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3314   }\r
3315   if(logoHeight) {\r
3316         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3317         if(appData.autoLogo) {\r
3318           \r
3319           switch(gameMode) { // pick logos based on game mode\r
3320             case IcsObserving:\r
3321                 whiteLogo = second.programLogo; // ICS logo\r
3322                 blackLogo = second.programLogo;\r
3323             default:\r
3324                 break;\r
3325             case IcsPlayingWhite:\r
3326                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3327                 blackLogo = second.programLogo; // ICS logo\r
3328                 break;\r
3329             case IcsPlayingBlack:\r
3330                 whiteLogo = second.programLogo; // ICS logo\r
3331                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3332                 break;\r
3333             case TwoMachinesPlay:\r
3334                 if(first.twoMachinesColor[0] == 'b') {\r
3335                     whiteLogo = second.programLogo;\r
3336                     blackLogo = first.programLogo;\r
3337                 }\r
3338                 break;\r
3339             case MachinePlaysWhite:\r
3340                 blackLogo = userLogo;\r
3341                 break;\r
3342             case MachinePlaysBlack:\r
3343                 whiteLogo = userLogo;\r
3344                 blackLogo = first.programLogo;\r
3345           }\r
3346         }\r
3347         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3348         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3349   }\r
3350 \r
3351   if( appData.highlightMoveWithArrow ) {\r
3352     DrawArrowHighlight(hdcmem);\r
3353   }\r
3354 \r
3355   DrawCoordsOnDC(hdcmem);\r
3356 \r
3357   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
3358                  /* to make sure lastDrawn contains what is actually drawn */\r
3359 \r
3360   /* Put the dragged piece back into place and draw it (out of place!) */\r
3361     if (dragged_piece != EmptySquare) {\r
3362     /* [HGM] or restack */\r
3363     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3364                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3365     else\r
3366     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3367                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3368     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3369     x = dragInfo.pos.x - squareSize / 2;\r
3370     y = dragInfo.pos.y - squareSize / 2;\r
3371     DrawPieceOnDC(hdcmem, dragged_piece,\r
3372                   ((int) dragged_piece < (int) BlackPawn), \r
3373                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3374   }   \r
3375   \r
3376   /* Put the animated piece back into place and draw it */\r
3377   if (animInfo.piece != EmptySquare) {\r
3378     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3379     x = boardRect.left + animInfo.pos.x;\r
3380     y = boardRect.top + animInfo.pos.y;\r
3381     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3382                   ((int) animInfo.piece < (int) BlackPawn),\r
3383                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3384   }\r
3385 \r
3386   /* Release the bufferBitmap by selecting in the old bitmap \r
3387    * and delete the memory DC\r
3388    */\r
3389   SelectObject(hdcmem, oldBitmap);\r
3390   DeleteDC(hdcmem);\r
3391 \r
3392   /* Set clipping on the target DC */\r
3393   if (!fullrepaint) {\r
3394     SelectClipRgn(hdc, clips[0]);\r
3395     for (x = 1; x < num_clips; x++) {\r
3396       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3397         abort();   // this should never ever happen!\r
3398     } \r
3399   }\r
3400 \r
3401   /* Copy the new bitmap onto the screen in one go.\r
3402    * This way we avoid any flickering\r
3403    */\r
3404   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3405   BitBlt(hdc, boardRect.left, boardRect.top,\r
3406          boardRect.right - boardRect.left,\r
3407          boardRect.bottom - boardRect.top,\r
3408          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3409   if(saveDiagFlag) { \r
3410     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3411     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3412 \r
3413     GetObject(bufferBitmap, sizeof(b), &b);\r
3414     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3415         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3416         bih.biWidth = b.bmWidth;\r
3417         bih.biHeight = b.bmHeight;\r
3418         bih.biPlanes = 1;\r
3419         bih.biBitCount = b.bmBitsPixel;\r
3420         bih.biCompression = 0;\r
3421         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3422         bih.biXPelsPerMeter = 0;\r
3423         bih.biYPelsPerMeter = 0;\r
3424         bih.biClrUsed = 0;\r
3425         bih.biClrImportant = 0;\r
3426 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3427 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3428         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3429 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3430 \r
3431         wb = b.bmWidthBytes;\r
3432         // count colors\r
3433         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3434                 int k = ((int*) pData)[i];\r
3435                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3436                 if(j >= 16) break;\r
3437                 color[j] = k;\r
3438                 if(j >= nrColors) nrColors = j+1;\r
3439         }\r
3440         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3441                 INT p = 0;\r
3442                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3443                     for(w=0; w<(wb>>2); w+=2) {\r
3444                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3445                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3446                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3447                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3448                         pData[p++] = m | j<<4;\r
3449                     }\r
3450                     while(p&3) pData[p++] = 0;\r
3451                 }\r
3452                 fac = 3;\r
3453                 wb = ((wb+31)>>5)<<2;\r
3454         }\r
3455         // write BITMAPFILEHEADER\r
3456         fprintf(diagFile, "BM");\r
3457         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3458         fputDW(diagFile, 0);\r
3459         fputDW(diagFile, 0x36 + (fac?64:0));\r
3460         // write BITMAPINFOHEADER\r
3461         fputDW(diagFile, 40);\r
3462         fputDW(diagFile, b.bmWidth);\r
3463         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3464         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3465         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3466         fputDW(diagFile, 0);\r
3467         fputDW(diagFile, 0);\r
3468         fputDW(diagFile, 0);\r
3469         fputDW(diagFile, 0);\r
3470         fputDW(diagFile, 0);\r
3471         fputDW(diagFile, 0);\r
3472         // write color table\r
3473         if(fac)\r
3474         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3475         // write bitmap data\r
3476         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3477                 fputc(pData[i], diagFile);\r
3478      }\r
3479   }\r
3480 \r
3481   SelectObject(tmphdc, oldBitmap);\r
3482 \r
3483   /* Massive cleanup */\r
3484   for (x = 0; x < num_clips; x++)\r
3485     DeleteObject(clips[x]);\r
3486 \r
3487   DeleteDC(tmphdc);\r
3488   DeleteObject(bufferBitmap);\r
3489 \r
3490   if (releaseDC) \r
3491     ReleaseDC(hwndMain, hdc);\r
3492   \r
3493   if (lastDrawnFlipView != flipView) {\r
3494     if (flipView)\r
3495       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3496     else\r
3497       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3498   }\r
3499 \r
3500 /*  CopyBoard(lastDrawn, board);*/\r
3501   lastDrawnHighlight = highlightInfo;\r
3502   lastDrawnPremove   = premoveHighlightInfo;\r
3503   lastDrawnFlipView = flipView;\r
3504   lastDrawnValid = 1;\r
3505 }\r
3506 \r
3507 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3508 int\r
3509 SaveDiagram(f)\r
3510      FILE *f;\r
3511 {\r
3512     saveDiagFlag = 1; diagFile = f;\r
3513     HDCDrawPosition(NULL, TRUE, NULL);\r
3514 \r
3515     saveDiagFlag = 0;\r
3516 \r
3517 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3518     \r
3519     fclose(f);\r
3520     return TRUE;\r
3521 }\r
3522 \r
3523 \r
3524 /*---------------------------------------------------------------------------*\\r
3525 | CLIENT PAINT PROCEDURE\r
3526 |   This is the main event-handler for the WM_PAINT message.\r
3527 |\r
3528 \*---------------------------------------------------------------------------*/\r
3529 VOID\r
3530 PaintProc(HWND hwnd)\r
3531 {\r
3532   HDC         hdc;\r
3533   PAINTSTRUCT ps;\r
3534   HFONT       oldFont;\r
3535 \r
3536   if((hdc = BeginPaint(hwnd, &ps))) {\r
3537     if (IsIconic(hwnd)) {\r
3538       DrawIcon(hdc, 2, 2, iconCurrent);\r
3539     } else {\r
3540       if (!appData.monoMode) {\r
3541         SelectPalette(hdc, hPal, FALSE);\r
3542         RealizePalette(hdc);\r
3543       }\r
3544       HDCDrawPosition(hdc, 1, NULL);\r
3545       oldFont =\r
3546         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3547       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3548                  ETO_CLIPPED|ETO_OPAQUE,\r
3549                  &messageRect, messageText, strlen(messageText), NULL);\r
3550       SelectObject(hdc, oldFont);\r
3551       DisplayBothClocks();\r
3552     }\r
3553     EndPaint(hwnd,&ps);\r
3554   }\r
3555 \r
3556   return;\r
3557 }\r
3558 \r
3559 \r
3560 /*\r
3561  * If the user selects on a border boundary, return -1; if off the board,\r
3562  *   return -2.  Otherwise map the event coordinate to the square.\r
3563  * The offset boardRect.left or boardRect.top must already have been\r
3564  *   subtracted from x.\r
3565  */\r
3566 int EventToSquare(x, limit)\r
3567      int x, limit;\r
3568 {\r
3569   if (x <= 0)\r
3570     return -2;\r
3571   if (x < lineGap)\r
3572     return -1;\r
3573   x -= lineGap;\r
3574   if ((x % (squareSize + lineGap)) >= squareSize)\r
3575     return -1;\r
3576   x /= (squareSize + lineGap);\r
3577     if (x >= limit)\r
3578     return -2;\r
3579   return x;\r
3580 }\r
3581 \r
3582 typedef struct {\r
3583   char piece;\r
3584   int command;\r
3585   char* name;\r
3586 } DropEnable;\r
3587 \r
3588 DropEnable dropEnables[] = {\r
3589   { 'P', DP_Pawn, "Pawn" },\r
3590   { 'N', DP_Knight, "Knight" },\r
3591   { 'B', DP_Bishop, "Bishop" },\r
3592   { 'R', DP_Rook, "Rook" },\r
3593   { 'Q', DP_Queen, "Queen" },\r
3594 };\r
3595 \r
3596 VOID\r
3597 SetupDropMenu(HMENU hmenu)\r
3598 {\r
3599   int i, count, enable;\r
3600   char *p;\r
3601   extern char white_holding[], black_holding[];\r
3602   char item[MSG_SIZ];\r
3603 \r
3604   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
3605     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
3606                dropEnables[i].piece);\r
3607     count = 0;\r
3608     while (p && *p++ == dropEnables[i].piece) count++;\r
3609     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
3610     enable = count > 0 || !appData.testLegality\r
3611       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
3612                       && !appData.icsActive);\r
3613     ModifyMenu(hmenu, dropEnables[i].command,\r
3614                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
3615                dropEnables[i].command, item);\r
3616   }\r
3617 }\r
3618 \r
3619 void DragPieceBegin(int x, int y)\r
3620 {\r
3621       dragInfo.lastpos.x = boardRect.left + x;\r
3622       dragInfo.lastpos.y = boardRect.top + y;\r
3623       dragInfo.from.x = fromX;\r
3624       dragInfo.from.y = fromY;\r
3625       dragInfo.start = dragInfo.from;\r
3626       SetCapture(hwndMain);\r
3627 }\r
3628 \r
3629 void DragPieceEnd(int x, int y)\r
3630 {\r
3631     ReleaseCapture();\r
3632     dragInfo.start.x = dragInfo.start.y = -1;\r
3633     dragInfo.from = dragInfo.start;\r
3634     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3635 }\r
3636 \r
3637 /* Event handler for mouse messages */\r
3638 VOID\r
3639 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3640 {\r
3641   int x, y;\r
3642   POINT pt;\r
3643   static int recursive = 0;\r
3644   HMENU hmenu;\r
3645   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
3646 \r
3647   if (recursive) {\r
3648     if (message == WM_MBUTTONUP) {\r
3649       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
3650          to the middle button: we simulate pressing the left button too!\r
3651          */\r
3652       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
3653       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
3654     }\r
3655     return;\r
3656   }\r
3657   recursive++;\r
3658   \r
3659   pt.x = LOWORD(lParam);\r
3660   pt.y = HIWORD(lParam);\r
3661   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
3662   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
3663   if (!flipView && y >= 0) {\r
3664     y = BOARD_HEIGHT - 1 - y;\r
3665   }\r
3666   if (flipView && x >= 0) {\r
3667     x = BOARD_WIDTH - 1 - x;\r
3668   }\r
3669 \r
3670   switch (message) {\r
3671   case WM_LBUTTONDOWN:\r
3672       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3673         if (gameMode == EditPosition) {\r
3674           SetWhiteToPlayEvent();\r
3675         } else if (gameMode == IcsPlayingBlack ||\r
3676                    gameMode == MachinePlaysWhite) {\r
3677           CallFlagEvent();\r
3678         } else if (gameMode == EditGame) {\r
3679           AdjustClock(flipClock, -1);\r
3680         }\r
3681       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3682         if (gameMode == EditPosition) {\r
3683           SetBlackToPlayEvent();\r
3684         } else if (gameMode == IcsPlayingWhite ||\r
3685                    gameMode == MachinePlaysBlack) {\r
3686           CallFlagEvent();\r
3687         } else if (gameMode == EditGame) {\r
3688           AdjustClock(!flipClock, -1);\r
3689         }\r
3690       }\r
3691       dragInfo.start.x = dragInfo.start.y = -1;\r
3692       dragInfo.from = dragInfo.start;\r
3693     if(fromX == -1 && frozen) { // not sure where this is for\r
3694                 fromX = fromY = -1; \r
3695       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
3696       break;\r
3697     }\r
3698       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
3699       DrawPosition(TRUE, NULL);\r
3700     break;\r
3701 \r
3702   case WM_LBUTTONUP:\r
3703       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
3704       DrawPosition(TRUE, NULL);\r
3705     break;\r
3706 \r
3707   case WM_MOUSEMOVE:\r
3708     if ((appData.animateDragging || appData.highlightDragging)\r
3709         && (wParam & MK_LBUTTON)\r
3710         && dragInfo.from.x >= 0) \r
3711     {\r
3712       BOOL full_repaint = FALSE;\r
3713 \r
3714       if (appData.animateDragging) {\r
3715         dragInfo.pos = pt;\r
3716       }\r
3717       if (appData.highlightDragging) {\r
3718         SetHighlights(fromX, fromY, x, y);\r
3719         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
3720             full_repaint = TRUE;\r
3721         }\r
3722       }\r
3723       \r
3724       DrawPosition( full_repaint, NULL);\r
3725       \r
3726       dragInfo.lastpos = dragInfo.pos;\r
3727     }\r
3728     break;\r
3729 \r
3730   case WM_MOUSEWHEEL: // [DM]\r
3731     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
3732        /* Mouse Wheel is being rolled forward\r
3733         * Play moves forward\r
3734         */\r
3735        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
3736                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
3737        /* Mouse Wheel is being rolled backward\r
3738         * Play moves backward\r
3739         */\r
3740        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
3741                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
3742     }\r
3743     break;\r
3744 \r
3745   case WM_MBUTTONDOWN:\r
3746   case WM_RBUTTONDOWN:\r
3747     ErrorPopDown();\r
3748     ReleaseCapture();\r
3749     fromX = fromY = -1;\r
3750     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3751     dragInfo.start.x = dragInfo.start.y = -1;\r
3752     dragInfo.from = dragInfo.start;\r
3753     dragInfo.lastpos = dragInfo.pos;\r
3754     if (appData.highlightDragging) {\r
3755       ClearHighlights();\r
3756     }\r
3757     if(y == -2) {\r
3758       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
3759       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
3760           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
3761       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
3762           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
3763       }\r
3764     }\r
3765     DrawPosition(TRUE, NULL);\r
3766 \r
3767     switch (gameMode) {\r
3768     case EditPosition:\r
3769     case IcsExamining:\r
3770       if (x < 0 || y < 0) break;\r
3771       fromX = x;\r
3772       fromY = y;\r
3773       if (message == WM_MBUTTONDOWN) {\r
3774         buttonCount = 3;  /* even if system didn't think so */\r
3775         if (wParam & MK_SHIFT) \r
3776           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3777         else\r
3778           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3779       } else { /* message == WM_RBUTTONDOWN */\r
3780         /* Just have one menu, on the right button.  Windows users don't\r
3781            think to try the middle one, and sometimes other software steals\r
3782            it, or it doesn't really exist. */\r
3783         if(gameInfo.variant != VariantShogi)\r
3784             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3785         else\r
3786             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
3787       }\r
3788       break;\r
3789     case IcsPlayingWhite:\r
3790     case IcsPlayingBlack:\r
3791     case EditGame:\r
3792     case MachinePlaysWhite:\r
3793     case MachinePlaysBlack:\r
3794       if (appData.testLegality &&\r
3795           gameInfo.variant != VariantBughouse &&\r
3796           gameInfo.variant != VariantCrazyhouse) break;\r
3797       if (x < 0 || y < 0) break;\r
3798       fromX = x;\r
3799       fromY = y;\r
3800       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3801       SetupDropMenu(hmenu);\r
3802       MenuPopup(hwnd, pt, hmenu, -1);\r
3803       break;\r
3804     default:\r
3805       break;\r
3806     }\r
3807     break;\r
3808   }\r
3809 \r
3810   recursive--;\r
3811 }\r
3812 \r
3813 /* Preprocess messages for buttons in main window */\r
3814 LRESULT CALLBACK\r
3815 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3816 {\r
3817   int id = GetWindowLong(hwnd, GWL_ID);\r
3818   int i, dir;\r
3819 \r
3820   for (i=0; i<N_BUTTONS; i++) {\r
3821     if (buttonDesc[i].id == id) break;\r
3822   }\r
3823   if (i == N_BUTTONS) return 0;\r
3824   switch (message) {\r
3825   case WM_KEYDOWN:\r
3826     switch (wParam) {\r
3827     case VK_LEFT:\r
3828     case VK_RIGHT:\r
3829       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3830       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3831       return TRUE;\r
3832     }\r
3833     break;\r
3834   case WM_CHAR:\r
3835     switch (wParam) {\r
3836     case '\r':\r
3837       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3838       return TRUE;\r
3839     default:\r
3840       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
3841         // [HGM] movenum: only letters or leading zero should go to ICS input\r
3842         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3843         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3844         SetFocus(h);\r
3845         SendMessage(h, WM_CHAR, wParam, lParam);\r
3846         return TRUE;\r
3847       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3848         PopUpMoveDialog((char)wParam);\r
3849       }\r
3850       break;\r
3851     }\r
3852     break;\r
3853   }\r
3854   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3855 }\r
3856 \r
3857 /* Process messages for Promotion dialog box */\r
3858 LRESULT CALLBACK\r
3859 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3860 {\r
3861   char promoChar;\r
3862 \r
3863   switch (message) {\r
3864   case WM_INITDIALOG: /* message: initialize dialog box */\r
3865     /* Center the dialog over the application window */\r
3866     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
3867     ShowWindow(GetDlgItem(hDlg, PB_King), \r
3868       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
3869        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
3870                SW_SHOW : SW_HIDE);\r
3871     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
3872     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
3873        ((PieceToChar(WhiteAngel) >= 'A' &&\r
3874          PieceToChar(WhiteAngel) != '~') ||\r
3875         (PieceToChar(BlackAngel) >= 'A' &&\r
3876          PieceToChar(BlackAngel) != '~')   ) ?\r
3877                SW_SHOW : SW_HIDE);\r
3878     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
3879        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
3880          PieceToChar(WhiteMarshall) != '~') ||\r
3881         (PieceToChar(BlackMarshall) >= 'A' &&\r
3882          PieceToChar(BlackMarshall) != '~')   ) ?\r
3883                SW_SHOW : SW_HIDE);\r
3884     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
3885     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
3886        gameInfo.variant != VariantShogi ?\r
3887                SW_SHOW : SW_HIDE);\r
3888     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
3889        gameInfo.variant != VariantShogi ?\r
3890                SW_SHOW : SW_HIDE);\r
3891     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
3892        gameInfo.variant == VariantShogi ?\r
3893                SW_SHOW : SW_HIDE);\r
3894     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
3895        gameInfo.variant == VariantShogi ?\r
3896                SW_SHOW : SW_HIDE);\r
3897     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
3898        gameInfo.variant == VariantSuper ?\r
3899                SW_SHOW : SW_HIDE);\r
3900     return TRUE;\r
3901 \r
3902   case WM_COMMAND: /* message: received a command */\r
3903     switch (LOWORD(wParam)) {\r
3904     case IDCANCEL:\r
3905       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3906       ClearHighlights();\r
3907       DrawPosition(FALSE, NULL);\r
3908       return TRUE;\r
3909     case PB_King:\r
3910       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
3911       break;\r
3912     case PB_Queen:\r
3913       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
3914       break;\r
3915     case PB_Rook:\r
3916       promoChar = PieceToChar(BlackRook);\r
3917       break;\r
3918     case PB_Bishop:\r
3919       promoChar = PieceToChar(BlackBishop);\r
3920       break;\r
3921     case PB_Chancellor:\r
3922       promoChar = PieceToChar(BlackMarshall);\r
3923       break;\r
3924     case PB_Archbishop:\r
3925       promoChar = PieceToChar(BlackAngel);\r
3926       break;\r
3927     case PB_Knight:\r
3928       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
3929       break;\r
3930     default:\r
3931       return FALSE;\r
3932     }\r
3933     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3934     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
3935        only show the popup when we are already sure the move is valid or\r
3936        legal. We pass a faulty move type, but the kludge is that FinishMove\r
3937        will figure out it is a promotion from the promoChar. */\r
3938     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3939     fromX = fromY = -1;\r
3940     if (!appData.highlightLastMove) {\r
3941       ClearHighlights();\r
3942       DrawPosition(FALSE, NULL);\r
3943     }\r
3944     return TRUE;\r
3945   }\r
3946   return FALSE;\r
3947 }\r
3948 \r
3949 /* Pop up promotion dialog */\r
3950 VOID\r
3951 PromotionPopup(HWND hwnd)\r
3952 {\r
3953   FARPROC lpProc;\r
3954 \r
3955   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3956   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3957     hwnd, (DLGPROC)lpProc);\r
3958   FreeProcInstance(lpProc);\r
3959 }\r
3960 \r
3961 void\r
3962 PromotionPopUp()\r
3963 {\r
3964   DrawPosition(TRUE, NULL);\r
3965   PromotionPopup(hwndMain);\r
3966 }\r
3967 \r
3968 /* Toggle ShowThinking */\r
3969 VOID\r
3970 ToggleShowThinking()\r
3971 {\r
3972   appData.showThinking = !appData.showThinking;\r
3973   ShowThinkingEvent();\r
3974 }\r
3975 \r
3976 VOID\r
3977 LoadGameDialog(HWND hwnd, char* title)\r
3978 {\r
3979   UINT number = 0;\r
3980   FILE *f;\r
3981   char fileTitle[MSG_SIZ];\r
3982   f = OpenFileDialog(hwnd, "rb", "",\r
3983                      appData.oldSaveStyle ? "gam" : "pgn",\r
3984                      GAME_FILT,\r
3985                      title, &number, fileTitle, NULL);\r
3986   if (f != NULL) {\r
3987     cmailMsgLoaded = FALSE;\r
3988     if (number == 0) {\r
3989       int error = GameListBuild(f);\r
3990       if (error) {\r
3991         DisplayError("Cannot build game list", error);\r
3992       } else if (!ListEmpty(&gameList) &&\r
3993                  ((ListGame *) gameList.tailPred)->number > 1) {\r
3994         GameListPopUp(f, fileTitle);\r
3995         return;\r
3996       }\r
3997       GameListDestroy();\r
3998       number = 1;\r
3999     }\r
4000     LoadGame(f, number, fileTitle, FALSE);\r
4001   }\r
4002 }\r
4003 \r
4004 int get_term_width()\r
4005 {\r
4006     HDC hdc;\r
4007     TEXTMETRIC tm;\r
4008     RECT rc;\r
4009     HFONT hfont, hold_font;\r
4010     LOGFONT lf;\r
4011     HWND hText;\r
4012 \r
4013     if (hwndConsole)\r
4014         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4015     else\r
4016         return 79;\r
4017 \r
4018     // get the text metrics\r
4019     hdc = GetDC(hText);\r
4020     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4021     if (consoleCF.dwEffects & CFE_BOLD)\r
4022         lf.lfWeight = FW_BOLD;\r
4023     if (consoleCF.dwEffects & CFE_ITALIC)\r
4024         lf.lfItalic = TRUE;\r
4025     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4026         lf.lfStrikeOut = TRUE;\r
4027     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4028         lf.lfUnderline = TRUE;\r
4029     hfont = CreateFontIndirect(&lf);\r
4030     hold_font = SelectObject(hdc, hfont);\r
4031     GetTextMetrics(hdc, &tm);\r
4032     SelectObject(hdc, hold_font);\r
4033     DeleteObject(hfont);\r
4034     ReleaseDC(hText, hdc);\r
4035 \r
4036     // get the rectangle\r
4037     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4038 \r
4039     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4040 }\r
4041 \r
4042 void UpdateICSWidth(HWND hText)\r
4043 {\r
4044     LONG old_width, new_width;\r
4045 \r
4046     new_width = get_term_width(hText, FALSE);\r
4047     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4048     if (new_width != old_width)\r
4049     {\r
4050         ics_update_width(new_width);\r
4051         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4052     }\r
4053 }\r
4054 \r
4055 VOID\r
4056 ChangedConsoleFont()\r
4057 {\r
4058   CHARFORMAT cfmt;\r
4059   CHARRANGE tmpsel, sel;\r
4060   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4061   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4062   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4063   PARAFORMAT paraf;\r
4064 \r
4065   cfmt.cbSize = sizeof(CHARFORMAT);\r
4066   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4067   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4068   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4069    * size.  This was undocumented in the version of MSVC++ that I had\r
4070    * when I wrote the code, but is apparently documented now.\r
4071    */\r
4072   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4073   cfmt.bCharSet = f->lf.lfCharSet;\r
4074   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4075   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4076   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4077   /* Why are the following seemingly needed too? */\r
4078   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4079   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4080   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4081   tmpsel.cpMin = 0;\r
4082   tmpsel.cpMax = -1; /*999999?*/\r
4083   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4084   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4085   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4086    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4087    */\r
4088   paraf.cbSize = sizeof(paraf);\r
4089   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4090   paraf.dxStartIndent = 0;\r
4091   paraf.dxOffset = WRAP_INDENT;\r
4092   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4093   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4094   UpdateICSWidth(hText);\r
4095 }\r
4096 \r
4097 /*---------------------------------------------------------------------------*\\r
4098  *\r
4099  * Window Proc for main window\r
4100  *\r
4101 \*---------------------------------------------------------------------------*/\r
4102 \r
4103 /* Process messages for main window, etc. */\r
4104 LRESULT CALLBACK\r
4105 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4106 {\r
4107   FARPROC lpProc;\r
4108   int wmId, wmEvent;\r
4109   char *defName;\r
4110   FILE *f;\r
4111   UINT number;\r
4112   char fileTitle[MSG_SIZ];\r
4113   char buf[MSG_SIZ];\r
4114   static SnapData sd;\r
4115 \r
4116   switch (message) {\r
4117 \r
4118   case WM_PAINT: /* message: repaint portion of window */\r
4119     PaintProc(hwnd);\r
4120     break;\r
4121 \r
4122   case WM_ERASEBKGND:\r
4123     if (IsIconic(hwnd)) {\r
4124       /* Cheat; change the message */\r
4125       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4126     } else {\r
4127       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4128     }\r
4129     break;\r
4130 \r
4131   case WM_LBUTTONDOWN:\r
4132   case WM_MBUTTONDOWN:\r
4133   case WM_RBUTTONDOWN:\r
4134   case WM_LBUTTONUP:\r
4135   case WM_MBUTTONUP:\r
4136   case WM_RBUTTONUP:\r
4137   case WM_MOUSEMOVE:\r
4138   case WM_MOUSEWHEEL:\r
4139     MouseEvent(hwnd, message, wParam, lParam);\r
4140     break;\r
4141 \r
4142   JAWS_KB_NAVIGATION\r
4143 \r
4144   case WM_CHAR:\r
4145     \r
4146     JAWS_ALT_INTERCEPT\r
4147 \r
4148     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4149         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4150         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4151         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4152         SetFocus(h);\r
4153         SendMessage(h, message, wParam, lParam);\r
4154     } else if(lParam != KF_REPEAT) {\r
4155         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4156                 PopUpMoveDialog((char)wParam);\r
4157         } else if((char)wParam == 003) CopyGameToClipboard();\r
4158          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4159     }\r
4160 \r
4161     break;\r
4162 \r
4163   case WM_PALETTECHANGED:\r
4164     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4165       int nnew;\r
4166       HDC hdc = GetDC(hwndMain);\r
4167       SelectPalette(hdc, hPal, TRUE);\r
4168       nnew = RealizePalette(hdc);\r
4169       if (nnew > 0) {\r
4170         paletteChanged = TRUE;\r
4171         InvalidateRect(hwnd, &boardRect, FALSE);\r
4172       }\r
4173       ReleaseDC(hwnd, hdc);\r
4174     }\r
4175     break;\r
4176 \r
4177   case WM_QUERYNEWPALETTE:\r
4178     if (!appData.monoMode /*&& paletteChanged*/) {\r
4179       int nnew;\r
4180       HDC hdc = GetDC(hwndMain);\r
4181       paletteChanged = FALSE;\r
4182       SelectPalette(hdc, hPal, FALSE);\r
4183       nnew = RealizePalette(hdc);\r
4184       if (nnew > 0) {\r
4185         InvalidateRect(hwnd, &boardRect, FALSE);\r
4186       }\r
4187       ReleaseDC(hwnd, hdc);\r
4188       return TRUE;\r
4189     }\r
4190     return FALSE;\r
4191 \r
4192   case WM_COMMAND: /* message: command from application menu */\r
4193     wmId    = LOWORD(wParam);\r
4194     wmEvent = HIWORD(wParam);\r
4195 \r
4196     switch (wmId) {\r
4197     case IDM_NewGame:\r
4198       ResetGameEvent();\r
4199       SAY("new game enter a move to play against the computer with white");\r
4200       break;\r
4201 \r
4202     case IDM_NewGameFRC:\r
4203       if( NewGameFRC() == 0 ) {\r
4204         ResetGameEvent();\r
4205       }\r
4206       break;\r
4207 \r
4208     case IDM_NewVariant:\r
4209       NewVariantPopup(hwnd);\r
4210       break;\r
4211 \r
4212     case IDM_LoadGame:\r
4213       LoadGameDialog(hwnd, "Load Game from File");\r
4214       break;\r
4215 \r
4216     case IDM_LoadNextGame:\r
4217       ReloadGame(1);\r
4218       break;\r
4219 \r
4220     case IDM_LoadPrevGame:\r
4221       ReloadGame(-1);\r
4222       break;\r
4223 \r
4224     case IDM_ReloadGame:\r
4225       ReloadGame(0);\r
4226       break;\r
4227 \r
4228     case IDM_LoadPosition:\r
4229       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4230         Reset(FALSE, TRUE);\r
4231       }\r
4232       number = 1;\r
4233       f = OpenFileDialog(hwnd, "rb", "",\r
4234                          appData.oldSaveStyle ? "pos" : "fen",\r
4235                          POSITION_FILT,\r
4236                          "Load Position from File", &number, fileTitle, NULL);\r
4237       if (f != NULL) {\r
4238         LoadPosition(f, number, fileTitle);\r
4239       }\r
4240       break;\r
4241 \r
4242     case IDM_LoadNextPosition:\r
4243       ReloadPosition(1);\r
4244       break;\r
4245 \r
4246     case IDM_LoadPrevPosition:\r
4247       ReloadPosition(-1);\r
4248       break;\r
4249 \r
4250     case IDM_ReloadPosition:\r
4251       ReloadPosition(0);\r
4252       break;\r
4253 \r
4254     case IDM_SaveGame:\r
4255       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4256       f = OpenFileDialog(hwnd, "a", defName,\r
4257                          appData.oldSaveStyle ? "gam" : "pgn",\r
4258                          GAME_FILT,\r
4259                          "Save Game to File", NULL, fileTitle, NULL);\r
4260       if (f != NULL) {\r
4261         SaveGame(f, 0, "");\r
4262       }\r
4263       break;\r
4264 \r
4265     case IDM_SavePosition:\r
4266       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4267       f = OpenFileDialog(hwnd, "a", defName,\r
4268                          appData.oldSaveStyle ? "pos" : "fen",\r
4269                          POSITION_FILT,\r
4270                          "Save Position to File", NULL, fileTitle, NULL);\r
4271       if (f != NULL) {\r
4272         SavePosition(f, 0, "");\r
4273       }\r
4274       break;\r
4275 \r
4276     case IDM_SaveDiagram:\r
4277       defName = "diagram";\r
4278       f = OpenFileDialog(hwnd, "wb", defName,\r
4279                          "bmp",\r
4280                          DIAGRAM_FILT,\r
4281                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4282       if (f != NULL) {\r
4283         SaveDiagram(f);\r
4284       }\r
4285       break;\r
4286 \r
4287     case IDM_CopyGame:\r
4288       CopyGameToClipboard();\r
4289       break;\r
4290 \r
4291     case IDM_PasteGame:\r
4292       PasteGameFromClipboard();\r
4293       break;\r
4294 \r
4295     case IDM_CopyGameListToClipboard:\r
4296       CopyGameListToClipboard();\r
4297       break;\r
4298 \r
4299     /* [AS] Autodetect FEN or PGN data */\r
4300     case IDM_PasteAny:\r
4301       PasteGameOrFENFromClipboard();\r
4302       break;\r
4303 \r
4304     /* [AS] Move history */\r
4305     case IDM_ShowMoveHistory:\r
4306         if( MoveHistoryIsUp() ) {\r
4307             MoveHistoryPopDown();\r
4308         }\r
4309         else {\r
4310             MoveHistoryPopUp();\r
4311         }\r
4312         break;\r
4313 \r
4314     /* [AS] Eval graph */\r
4315     case IDM_ShowEvalGraph:\r
4316         if( EvalGraphIsUp() ) {\r
4317             EvalGraphPopDown();\r
4318         }\r
4319         else {\r
4320             EvalGraphPopUp();\r
4321             SetFocus(hwndMain);\r
4322         }\r
4323         break;\r
4324 \r
4325     /* [AS] Engine output */\r
4326     case IDM_ShowEngineOutput:\r
4327         if( EngineOutputIsUp() ) {\r
4328             EngineOutputPopDown();\r
4329         }\r
4330         else {\r
4331             EngineOutputPopUp();\r
4332         }\r
4333         break;\r
4334 \r
4335     /* [AS] User adjudication */\r
4336     case IDM_UserAdjudication_White:\r
4337         UserAdjudicationEvent( +1 );\r
4338         break;\r
4339 \r
4340     case IDM_UserAdjudication_Black:\r
4341         UserAdjudicationEvent( -1 );\r
4342         break;\r
4343 \r
4344     case IDM_UserAdjudication_Draw:\r
4345         UserAdjudicationEvent( 0 );\r
4346         break;\r
4347 \r
4348     /* [AS] Game list options dialog */\r
4349     case IDM_GameListOptions:\r
4350       GameListOptions();\r
4351       break;\r
4352 \r
4353     case IDM_NewChat:\r
4354       ChatPopUp();\r
4355       break;\r
4356 \r
4357     case IDM_CopyPosition:\r
4358       CopyFENToClipboard();\r
4359       break;\r
4360 \r
4361     case IDM_PastePosition:\r
4362       PasteFENFromClipboard();\r
4363       break;\r
4364 \r
4365     case IDM_MailMove:\r
4366       MailMoveEvent();\r
4367       break;\r
4368 \r
4369     case IDM_ReloadCMailMsg:\r
4370       Reset(TRUE, TRUE);\r
4371       ReloadCmailMsgEvent(FALSE);\r
4372       break;\r
4373 \r
4374     case IDM_Minimize:\r
4375       ShowWindow(hwnd, SW_MINIMIZE);\r
4376       break;\r
4377 \r
4378     case IDM_Exit:\r
4379       ExitEvent(0);\r
4380       break;\r
4381 \r
4382     case IDM_MachineWhite:\r
4383       MachineWhiteEvent();\r
4384       /*\r
4385        * refresh the tags dialog only if it's visible\r
4386        */\r
4387       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4388           char *tags;\r
4389           tags = PGNTags(&gameInfo);\r
4390           TagsPopUp(tags, CmailMsg());\r
4391           free(tags);\r
4392       }\r
4393       SAY("computer starts playing white");\r
4394       break;\r
4395 \r
4396     case IDM_MachineBlack:\r
4397       MachineBlackEvent();\r
4398       /*\r
4399        * refresh the tags dialog only if it's visible\r
4400        */\r
4401       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4402           char *tags;\r
4403           tags = PGNTags(&gameInfo);\r
4404           TagsPopUp(tags, CmailMsg());\r
4405           free(tags);\r
4406       }\r
4407       SAY("computer starts playing black");\r
4408       break;\r
4409 \r
4410     case IDM_TwoMachines:\r
4411       TwoMachinesEvent();\r
4412       /*\r
4413        * refresh the tags dialog only if it's visible\r
4414        */\r
4415       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4416           char *tags;\r
4417           tags = PGNTags(&gameInfo);\r
4418           TagsPopUp(tags, CmailMsg());\r
4419           free(tags);\r
4420       }\r
4421       SAY("programs start playing each other");\r
4422       break;\r
4423 \r
4424     case IDM_AnalysisMode:\r
4425       if (!first.analysisSupport) {\r
4426         sprintf(buf, "%s does not support analysis", first.tidy);\r
4427         DisplayError(buf, 0);\r
4428       } else {\r
4429         SAY("analyzing current position");\r
4430         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4431         if (appData.icsActive) {\r
4432                if (gameMode != IcsObserving) {\r
4433                        sprintf(buf, "You are not observing a game");\r
4434                        DisplayError(buf, 0);\r
4435                        /* secure check */\r
4436                        if (appData.icsEngineAnalyze) {\r
4437                                if (appData.debugMode) \r
4438                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4439                                ExitAnalyzeMode();\r
4440                                ModeHighlight();\r
4441                                break;\r
4442                        }\r
4443                        break;\r
4444                } else {\r
4445                        /* if enable, user want disable icsEngineAnalyze */\r
4446                        if (appData.icsEngineAnalyze) {\r
4447                                ExitAnalyzeMode();\r
4448                                ModeHighlight();\r
4449                                break;\r
4450                        }\r
4451                        appData.icsEngineAnalyze = TRUE;\r
4452                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4453                }\r
4454         } \r
4455         if (!appData.showThinking) ToggleShowThinking();\r
4456         AnalyzeModeEvent();\r
4457       }\r
4458       break;\r
4459 \r
4460     case IDM_AnalyzeFile:\r
4461       if (!first.analysisSupport) {\r
4462         char buf[MSG_SIZ];\r
4463         sprintf(buf, "%s does not support analysis", first.tidy);\r
4464         DisplayError(buf, 0);\r
4465       } else {\r
4466         if (!appData.showThinking) ToggleShowThinking();\r
4467         AnalyzeFileEvent();\r
4468         LoadGameDialog(hwnd, "Analyze Game from File");\r
4469         AnalysisPeriodicEvent(1);\r
4470       }\r
4471       break;\r
4472 \r
4473     case IDM_IcsClient:\r
4474       IcsClientEvent();\r
4475       break;\r
4476 \r
4477     case IDM_EditGame:\r
4478       EditGameEvent();\r
4479       SAY("edit game");\r
4480       break;\r
4481 \r
4482     case IDM_EditPosition:\r
4483       EditPositionEvent();\r
4484       SAY("to set up a position type a FEN");\r
4485       break;\r
4486 \r
4487     case IDM_Training:\r
4488       TrainingEvent();\r
4489       break;\r
4490 \r
4491     case IDM_ShowGameList:\r
4492       ShowGameListProc();\r
4493       break;\r
4494 \r
4495     case IDM_EditTags:\r
4496       EditTagsProc();\r
4497       break;\r
4498 \r
4499     case IDM_EditComment:\r
4500       if (commentUp && editComment) {\r
4501         CommentPopDown();\r
4502       } else {\r
4503         EditCommentEvent();\r
4504       }\r
4505       break;\r
4506 \r
4507     case IDM_Pause:\r
4508       PauseEvent();\r
4509       break;\r
4510 \r
4511     case IDM_Accept:\r
4512       AcceptEvent();\r
4513       break;\r
4514 \r
4515     case IDM_Decline:\r
4516       DeclineEvent();\r
4517       break;\r
4518 \r
4519     case IDM_Rematch:\r
4520       RematchEvent();\r
4521       break;\r
4522 \r
4523     case IDM_CallFlag:\r
4524       CallFlagEvent();\r
4525       break;\r
4526 \r
4527     case IDM_Draw:\r
4528       DrawEvent();\r
4529       break;\r
4530 \r
4531     case IDM_Adjourn:\r
4532       AdjournEvent();\r
4533       break;\r
4534 \r
4535     case IDM_Abort:\r
4536       AbortEvent();\r
4537       break;\r
4538 \r
4539     case IDM_Resign:\r
4540       ResignEvent();\r
4541       break;\r
4542 \r
4543     case IDM_StopObserving:\r
4544       StopObservingEvent();\r
4545       break;\r
4546 \r
4547     case IDM_StopExamining:\r
4548       StopExaminingEvent();\r
4549       break;\r
4550 \r
4551     case IDM_TypeInMove:\r
4552       PopUpMoveDialog('\000');\r
4553       break;\r
4554 \r
4555     case IDM_TypeInName:\r
4556       PopUpNameDialog('\000');\r
4557       break;\r
4558 \r
4559     case IDM_Backward:\r
4560       BackwardEvent();\r
4561       SetFocus(hwndMain);\r
4562       break;\r
4563 \r
4564     JAWS_MENU_ITEMS\r
4565 \r
4566     case IDM_Forward:\r
4567       ForwardEvent();\r
4568       SetFocus(hwndMain);\r
4569       break;\r
4570 \r
4571     case IDM_ToStart:\r
4572       ToStartEvent();\r
4573       SetFocus(hwndMain);\r
4574       break;\r
4575 \r
4576     case IDM_ToEnd:\r
4577       ToEndEvent();\r
4578       SetFocus(hwndMain);\r
4579       break;\r
4580 \r
4581     case IDM_Revert:\r
4582       RevertEvent();\r
4583       break;\r
4584 \r
4585     case IDM_TruncateGame:\r
4586       TruncateGameEvent();\r
4587       break;\r
4588 \r
4589     case IDM_MoveNow:\r
4590       MoveNowEvent();\r
4591       break;\r
4592 \r
4593     case IDM_RetractMove:\r
4594       RetractMoveEvent();\r
4595       break;\r
4596 \r
4597     case IDM_FlipView:\r
4598       flipView = !flipView;\r
4599       DrawPosition(FALSE, NULL);\r
4600       break;\r
4601 \r
4602     case IDM_FlipClock:\r
4603       flipClock = !flipClock;\r
4604       DisplayBothClocks();\r
4605       DrawPosition(FALSE, NULL);\r
4606       break;\r
4607 \r
4608     case IDM_MuteSounds:\r
4609       mute = !mute; // [HGM] mute: keep track of global muting variable\r
4610       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
4611                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
4612       break;\r
4613 \r
4614     case IDM_GeneralOptions:\r
4615       GeneralOptionsPopup(hwnd);\r
4616       DrawPosition(TRUE, NULL);\r
4617       break;\r
4618 \r
4619     case IDM_BoardOptions:\r
4620       BoardOptionsPopup(hwnd);\r
4621       break;\r
4622 \r
4623     case IDM_EnginePlayOptions:\r
4624       EnginePlayOptionsPopup(hwnd);\r
4625       break;\r
4626 \r
4627     case IDM_Engine1Options:\r
4628       EngineOptionsPopup(hwnd, &first);\r
4629       break;\r
4630 \r
4631     case IDM_Engine2Options:\r
4632       EngineOptionsPopup(hwnd, &second);\r
4633       break;\r
4634 \r
4635     case IDM_OptionsUCI:\r
4636       UciOptionsPopup(hwnd);\r
4637       break;\r
4638 \r
4639     case IDM_IcsOptions:\r
4640       IcsOptionsPopup(hwnd);\r
4641       break;\r
4642 \r
4643     case IDM_Fonts:\r
4644       FontsOptionsPopup(hwnd);\r
4645       break;\r
4646 \r
4647     case IDM_Sounds:\r
4648       SoundOptionsPopup(hwnd);\r
4649       break;\r
4650 \r
4651     case IDM_CommPort:\r
4652       CommPortOptionsPopup(hwnd);\r
4653       break;\r
4654 \r
4655     case IDM_LoadOptions:\r
4656       LoadOptionsPopup(hwnd);\r
4657       break;\r
4658 \r
4659     case IDM_SaveOptions:\r
4660       SaveOptionsPopup(hwnd);\r
4661       break;\r
4662 \r
4663     case IDM_TimeControl:\r
4664       TimeControlOptionsPopup(hwnd);\r
4665       break;\r
4666 \r
4667     case IDM_SaveSettings:\r
4668       SaveSettings(settingsFileName);\r
4669       break;\r
4670 \r
4671     case IDM_SaveSettingsOnExit:\r
4672       saveSettingsOnExit = !saveSettingsOnExit;\r
4673       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
4674                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
4675                                          MF_CHECKED : MF_UNCHECKED));\r
4676       break;\r
4677 \r
4678     case IDM_Hint:\r
4679       HintEvent();\r
4680       break;\r
4681 \r
4682     case IDM_Book:\r
4683       BookEvent();\r
4684       break;\r
4685 \r
4686     case IDM_AboutGame:\r
4687       AboutGameEvent();\r
4688       break;\r
4689 \r
4690     case IDM_Debug:\r
4691       appData.debugMode = !appData.debugMode;\r
4692       if (appData.debugMode) {\r
4693         char dir[MSG_SIZ];\r
4694         GetCurrentDirectory(MSG_SIZ, dir);\r
4695         SetCurrentDirectory(installDir);\r
4696         debugFP = fopen(appData.nameOfDebugFile, "w");\r
4697         SetCurrentDirectory(dir);\r
4698         setbuf(debugFP, NULL);\r
4699       } else {\r
4700         fclose(debugFP);\r
4701         debugFP = NULL;\r
4702       }\r
4703       break;\r
4704 \r
4705     case IDM_HELPCONTENTS:\r
4706       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
4707           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
4708           MessageBox (GetFocus(),\r
4709                     "Unable to activate help",\r
4710                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4711       }\r
4712       break;\r
4713 \r
4714     case IDM_HELPSEARCH:\r
4715         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
4716             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
4717         MessageBox (GetFocus(),\r
4718                     "Unable to activate help",\r
4719                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4720       }\r
4721       break;\r
4722 \r
4723     case IDM_HELPHELP:\r
4724       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
4725         MessageBox (GetFocus(),\r
4726                     "Unable to activate help",\r
4727                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4728       }\r
4729       break;\r
4730 \r
4731     case IDM_ABOUT:\r
4732       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
4733       DialogBox(hInst, \r
4734         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
4735         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
4736       FreeProcInstance(lpProc);\r
4737       break;\r
4738 \r
4739     case IDM_DirectCommand1:\r
4740       AskQuestionEvent("Direct Command",\r
4741                        "Send to chess program:", "", "1");\r
4742       break;\r
4743     case IDM_DirectCommand2:\r
4744       AskQuestionEvent("Direct Command",\r
4745                        "Send to second chess program:", "", "2");\r
4746       break;\r
4747 \r
4748     case EP_WhitePawn:\r
4749       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
4750       fromX = fromY = -1;\r
4751       break;\r
4752 \r
4753     case EP_WhiteKnight:\r
4754       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
4755       fromX = fromY = -1;\r
4756       break;\r
4757 \r
4758     case EP_WhiteBishop:\r
4759       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
4760       fromX = fromY = -1;\r
4761       break;\r
4762 \r
4763     case EP_WhiteRook:\r
4764       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
4765       fromX = fromY = -1;\r
4766       break;\r
4767 \r
4768     case EP_WhiteQueen:\r
4769       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
4770       fromX = fromY = -1;\r
4771       break;\r
4772 \r
4773     case EP_WhiteFerz:\r
4774       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
4775       fromX = fromY = -1;\r
4776       break;\r
4777 \r
4778     case EP_WhiteWazir:\r
4779       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
4780       fromX = fromY = -1;\r
4781       break;\r
4782 \r
4783     case EP_WhiteAlfil:\r
4784       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
4785       fromX = fromY = -1;\r
4786       break;\r
4787 \r
4788     case EP_WhiteCannon:\r
4789       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
4790       fromX = fromY = -1;\r
4791       break;\r
4792 \r
4793     case EP_WhiteCardinal:\r
4794       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
4795       fromX = fromY = -1;\r
4796       break;\r
4797 \r
4798     case EP_WhiteMarshall:\r
4799       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
4800       fromX = fromY = -1;\r
4801       break;\r
4802 \r
4803     case EP_WhiteKing:\r
4804       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
4805       fromX = fromY = -1;\r
4806       break;\r
4807 \r
4808     case EP_BlackPawn:\r
4809       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
4810       fromX = fromY = -1;\r
4811       break;\r
4812 \r
4813     case EP_BlackKnight:\r
4814       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
4815       fromX = fromY = -1;\r
4816       break;\r
4817 \r
4818     case EP_BlackBishop:\r
4819       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
4820       fromX = fromY = -1;\r
4821       break;\r
4822 \r
4823     case EP_BlackRook:\r
4824       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
4825       fromX = fromY = -1;\r
4826       break;\r
4827 \r
4828     case EP_BlackQueen:\r
4829       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
4830       fromX = fromY = -1;\r
4831       break;\r
4832 \r
4833     case EP_BlackFerz:\r
4834       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
4835       fromX = fromY = -1;\r
4836       break;\r
4837 \r
4838     case EP_BlackWazir:\r
4839       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
4840       fromX = fromY = -1;\r
4841       break;\r
4842 \r
4843     case EP_BlackAlfil:\r
4844       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
4845       fromX = fromY = -1;\r
4846       break;\r
4847 \r
4848     case EP_BlackCannon:\r
4849       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
4850       fromX = fromY = -1;\r
4851       break;\r
4852 \r
4853     case EP_BlackCardinal:\r
4854       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
4855       fromX = fromY = -1;\r
4856       break;\r
4857 \r
4858     case EP_BlackMarshall:\r
4859       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
4860       fromX = fromY = -1;\r
4861       break;\r
4862 \r
4863     case EP_BlackKing:\r
4864       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
4865       fromX = fromY = -1;\r
4866       break;\r
4867 \r
4868     case EP_EmptySquare:\r
4869       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
4870       fromX = fromY = -1;\r
4871       break;\r
4872 \r
4873     case EP_ClearBoard:\r
4874       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
4875       fromX = fromY = -1;\r
4876       break;\r
4877 \r
4878     case EP_White:\r
4879       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
4880       fromX = fromY = -1;\r
4881       break;\r
4882 \r
4883     case EP_Black:\r
4884       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
4885       fromX = fromY = -1;\r
4886       break;\r
4887 \r
4888     case EP_Promote:\r
4889       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
4890       fromX = fromY = -1;\r
4891       break;\r
4892 \r
4893     case EP_Demote:\r
4894       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
4895       fromX = fromY = -1;\r
4896       break;\r
4897 \r
4898     case DP_Pawn:\r
4899       DropMenuEvent(WhitePawn, fromX, fromY);\r
4900       fromX = fromY = -1;\r
4901       break;\r
4902 \r
4903     case DP_Knight:\r
4904       DropMenuEvent(WhiteKnight, fromX, fromY);\r
4905       fromX = fromY = -1;\r
4906       break;\r
4907 \r
4908     case DP_Bishop:\r
4909       DropMenuEvent(WhiteBishop, fromX, fromY);\r
4910       fromX = fromY = -1;\r
4911       break;\r
4912 \r
4913     case DP_Rook:\r
4914       DropMenuEvent(WhiteRook, fromX, fromY);\r
4915       fromX = fromY = -1;\r
4916       break;\r
4917 \r
4918     case DP_Queen:\r
4919       DropMenuEvent(WhiteQueen, fromX, fromY);\r
4920       fromX = fromY = -1;\r
4921       break;\r
4922 \r
4923     default:\r
4924       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4925     }\r
4926     break;\r
4927 \r
4928   case WM_TIMER:\r
4929     switch (wParam) {\r
4930     case CLOCK_TIMER_ID:\r
4931       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
4932       clockTimerEvent = 0;\r
4933       DecrementClocks(); /* call into back end */\r
4934       break;\r
4935     case LOAD_GAME_TIMER_ID:\r
4936       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
4937       loadGameTimerEvent = 0;\r
4938       AutoPlayGameLoop(); /* call into back end */\r
4939       break;\r
4940     case ANALYSIS_TIMER_ID:\r
4941       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
4942                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
4943         AnalysisPeriodicEvent(0);\r
4944       } else {\r
4945         KillTimer(hwnd, analysisTimerEvent);\r
4946         analysisTimerEvent = 0;\r
4947       }\r
4948       break;\r
4949     case DELAYED_TIMER_ID:\r
4950       KillTimer(hwnd, delayedTimerEvent);\r
4951       delayedTimerEvent = 0;\r
4952       delayedTimerCallback();\r
4953       break;\r
4954     }\r
4955     break;\r
4956 \r
4957   case WM_USER_Input:\r
4958     InputEvent(hwnd, message, wParam, lParam);\r
4959     break;\r
4960 \r
4961   /* [AS] Also move "attached" child windows */\r
4962   case WM_WINDOWPOSCHANGING:\r
4963 \r
4964     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
4965         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
4966 \r
4967         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
4968             /* Window is moving */\r
4969             RECT rcMain;\r
4970 \r
4971 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
4972             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
4973             rcMain.right  = wpMain.x + wpMain.width;\r
4974             rcMain.top    = wpMain.y;\r
4975             rcMain.bottom = wpMain.y + wpMain.height;\r
4976             \r
4977             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
4978             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
4979             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
4980             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
4981             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
4982             wpMain.x = lpwp->x;\r
4983             wpMain.y = lpwp->y;\r
4984         }\r
4985     }\r
4986     break;\r
4987 \r
4988   /* [AS] Snapping */\r
4989   case WM_ENTERSIZEMOVE:\r
4990     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
4991     if (hwnd == hwndMain) {\r
4992       doingSizing = TRUE;\r
4993       lastSizing = 0;\r
4994     }\r
4995     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
4996     break;\r
4997 \r
4998   case WM_SIZING:\r
4999     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5000     if (hwnd == hwndMain) {\r
5001       lastSizing = wParam;\r
5002     }\r
5003     break;\r
5004 \r
5005   case WM_MOVING:\r
5006     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5007       return OnMoving( &sd, hwnd, wParam, lParam );\r
5008 \r
5009   case WM_EXITSIZEMOVE:\r
5010     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5011     if (hwnd == hwndMain) {\r
5012       RECT client;\r
5013       doingSizing = FALSE;\r
5014       InvalidateRect(hwnd, &boardRect, FALSE);\r
5015       GetClientRect(hwnd, &client);\r
5016       ResizeBoard(client.right, client.bottom, lastSizing);\r
5017       lastSizing = 0;\r
5018       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5019     }\r
5020     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5021     break;\r
5022 \r
5023   case WM_DESTROY: /* message: window being destroyed */\r
5024     PostQuitMessage(0);\r
5025     break;\r
5026 \r
5027   case WM_CLOSE:\r
5028     if (hwnd == hwndMain) {\r
5029       ExitEvent(0);\r
5030     }\r
5031     break;\r
5032 \r
5033   default:      /* Passes it on if unprocessed */\r
5034     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5035   }\r
5036   return 0;\r
5037 }\r
5038 \r
5039 /*---------------------------------------------------------------------------*\\r
5040  *\r
5041  * Misc utility routines\r
5042  *\r
5043 \*---------------------------------------------------------------------------*/\r
5044 \r
5045 /*\r
5046  * Decent random number generator, at least not as bad as Windows\r
5047  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5048  */\r
5049 unsigned int randstate;\r
5050 \r
5051 int\r
5052 myrandom(void)\r
5053 {\r
5054   randstate = randstate * 1664525 + 1013904223;\r
5055   return (int) randstate & 0x7fffffff;\r
5056 }\r
5057 \r
5058 void\r
5059 mysrandom(unsigned int seed)\r
5060 {\r
5061   randstate = seed;\r
5062 }\r
5063 \r
5064 \r
5065 /* \r
5066  * returns TRUE if user selects a different color, FALSE otherwise \r
5067  */\r
5068 \r
5069 BOOL\r
5070 ChangeColor(HWND hwnd, COLORREF *which)\r
5071 {\r
5072   static BOOL firstTime = TRUE;\r
5073   static DWORD customColors[16];\r
5074   CHOOSECOLOR cc;\r
5075   COLORREF newcolor;\r
5076   int i;\r
5077   ColorClass ccl;\r
5078 \r
5079   if (firstTime) {\r
5080     /* Make initial colors in use available as custom colors */\r
5081     /* Should we put the compiled-in defaults here instead? */\r
5082     i = 0;\r
5083     customColors[i++] = lightSquareColor & 0xffffff;\r
5084     customColors[i++] = darkSquareColor & 0xffffff;\r
5085     customColors[i++] = whitePieceColor & 0xffffff;\r
5086     customColors[i++] = blackPieceColor & 0xffffff;\r
5087     customColors[i++] = highlightSquareColor & 0xffffff;\r
5088     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5089 \r
5090     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5091       customColors[i++] = textAttribs[ccl].color;\r
5092     }\r
5093     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5094     firstTime = FALSE;\r
5095   }\r
5096 \r
5097   cc.lStructSize = sizeof(cc);\r
5098   cc.hwndOwner = hwnd;\r
5099   cc.hInstance = NULL;\r
5100   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5101   cc.lpCustColors = (LPDWORD) customColors;\r
5102   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5103 \r
5104   if (!ChooseColor(&cc)) return FALSE;\r
5105 \r
5106   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5107   if (newcolor == *which) return FALSE;\r
5108   *which = newcolor;\r
5109   return TRUE;\r
5110 \r
5111   /*\r
5112   InitDrawingColors();\r
5113   InvalidateRect(hwnd, &boardRect, FALSE);\r
5114   */\r
5115 }\r
5116 \r
5117 BOOLEAN\r
5118 MyLoadSound(MySound *ms)\r
5119 {\r
5120   BOOL ok = FALSE;\r
5121   struct stat st;\r
5122   FILE *f;\r
5123 \r
5124   if (ms->data) free(ms->data);\r
5125   ms->data = NULL;\r
5126 \r
5127   switch (ms->name[0]) {\r
5128   case NULLCHAR:\r
5129     /* Silence */\r
5130     ok = TRUE;\r
5131     break;\r
5132   case '$':\r
5133     /* System sound from Control Panel.  Don't preload here. */\r
5134     ok = TRUE;\r
5135     break;\r
5136   case '!':\r
5137     if (ms->name[1] == NULLCHAR) {\r
5138       /* "!" alone = silence */\r
5139       ok = TRUE;\r
5140     } else {\r
5141       /* Builtin wave resource.  Error if not found. */\r
5142       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5143       if (h == NULL) break;\r
5144       ms->data = (void *)LoadResource(hInst, h);\r
5145       if (h == NULL) break;\r
5146       ok = TRUE;\r
5147     }\r
5148     break;\r
5149   default:\r
5150     /* .wav file.  Error if not found. */\r
5151     f = fopen(ms->name, "rb");\r
5152     if (f == NULL) break;\r
5153     if (fstat(fileno(f), &st) < 0) break;\r
5154     ms->data = malloc(st.st_size);\r
5155     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5156     fclose(f);\r
5157     ok = TRUE;\r
5158     break;\r
5159   }\r
5160   if (!ok) {\r
5161     char buf[MSG_SIZ];\r
5162     sprintf(buf, "Error loading sound %s", ms->name);\r
5163     DisplayError(buf, GetLastError());\r
5164   }\r
5165   return ok;\r
5166 }\r
5167 \r
5168 BOOLEAN\r
5169 MyPlaySound(MySound *ms)\r
5170 {\r
5171   BOOLEAN ok = FALSE;\r
5172 \r
5173   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5174   switch (ms->name[0]) {\r
5175   case NULLCHAR:\r
5176         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5177     /* Silence */\r
5178     ok = TRUE;\r
5179     break;\r
5180   case '$':\r
5181     /* System sound from Control Panel (deprecated feature).\r
5182        "$" alone or an unset sound name gets default beep (still in use). */\r
5183     if (ms->name[1]) {\r
5184       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5185     }\r
5186     if (!ok) ok = MessageBeep(MB_OK);\r
5187     break; \r
5188   case '!':\r
5189     /* Builtin wave resource, or "!" alone for silence */\r
5190     if (ms->name[1]) {\r
5191       if (ms->data == NULL) return FALSE;\r
5192       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5193     } else {\r
5194       ok = TRUE;\r
5195     }\r
5196     break;\r
5197   default:\r
5198     /* .wav file.  Error if not found. */\r
5199     if (ms->data == NULL) return FALSE;\r
5200     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5201     break;\r
5202   }\r
5203   /* Don't print an error: this can happen innocently if the sound driver\r
5204      is busy; for instance, if another instance of WinBoard is playing\r
5205      a sound at about the same time. */\r
5206   return ok;\r
5207 }\r
5208 \r
5209 \r
5210 LRESULT CALLBACK\r
5211 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5212 {\r
5213   BOOL ok;\r
5214   OPENFILENAME *ofn;\r
5215   static UINT *number; /* gross that this is static */\r
5216 \r
5217   switch (message) {\r
5218   case WM_INITDIALOG: /* message: initialize dialog box */\r
5219     /* Center the dialog over the application window */\r
5220     ofn = (OPENFILENAME *) lParam;\r
5221     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5222       number = (UINT *) ofn->lCustData;\r
5223       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5224     } else {\r
5225       number = NULL;\r
5226     }\r
5227     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5228     return FALSE;  /* Allow for further processing */\r
5229 \r
5230   case WM_COMMAND:\r
5231     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5232       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5233     }\r
5234     return FALSE;  /* Allow for further processing */\r
5235   }\r
5236   return FALSE;\r
5237 }\r
5238 \r
5239 UINT APIENTRY\r
5240 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5241 {\r
5242   static UINT *number;\r
5243   OPENFILENAME *ofname;\r
5244   OFNOTIFY *ofnot;\r
5245   switch (uiMsg) {\r
5246   case WM_INITDIALOG:\r
5247     ofname = (OPENFILENAME *)lParam;\r
5248     number = (UINT *)(ofname->lCustData);\r
5249     break;\r
5250   case WM_NOTIFY:\r
5251     ofnot = (OFNOTIFY *)lParam;\r
5252     if (ofnot->hdr.code == CDN_FILEOK) {\r
5253       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5254     }\r
5255     break;\r
5256   }\r
5257   return 0;\r
5258 }\r
5259 \r
5260 \r
5261 FILE *\r
5262 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5263                char *nameFilt, char *dlgTitle, UINT *number,\r
5264                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5265 {\r
5266   OPENFILENAME openFileName;\r
5267   char buf1[MSG_SIZ];\r
5268   FILE *f;\r
5269 \r
5270   if (fileName == NULL) fileName = buf1;\r
5271   if (defName == NULL) {\r
5272     strcpy(fileName, "*.");\r
5273     strcat(fileName, defExt);\r
5274   } else {\r
5275     strcpy(fileName, defName);\r
5276   }\r
5277   if (fileTitle) strcpy(fileTitle, "");\r
5278   if (number) *number = 0;\r
5279 \r
5280   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5281   openFileName.hwndOwner         = hwnd;\r
5282   openFileName.hInstance         = (HANDLE) hInst;\r
5283   openFileName.lpstrFilter       = nameFilt;\r
5284   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5285   openFileName.nMaxCustFilter    = 0L;\r
5286   openFileName.nFilterIndex      = 1L;\r
5287   openFileName.lpstrFile         = fileName;\r
5288   openFileName.nMaxFile          = MSG_SIZ;\r
5289   openFileName.lpstrFileTitle    = fileTitle;\r
5290   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5291   openFileName.lpstrInitialDir   = NULL;\r
5292   openFileName.lpstrTitle        = dlgTitle;\r
5293   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5294     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5295     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5296     | (oldDialog ? 0 : OFN_EXPLORER);\r
5297   openFileName.nFileOffset       = 0;\r
5298   openFileName.nFileExtension    = 0;\r
5299   openFileName.lpstrDefExt       = defExt;\r
5300   openFileName.lCustData         = (LONG) number;\r
5301   openFileName.lpfnHook          = oldDialog ?\r
5302     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5303   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5304 \r
5305   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5306                         GetOpenFileName(&openFileName)) {\r
5307     /* open the file */\r
5308     f = fopen(openFileName.lpstrFile, write);\r
5309     if (f == NULL) {\r
5310       MessageBox(hwnd, "File open failed", NULL,\r
5311                  MB_OK|MB_ICONEXCLAMATION);\r
5312       return NULL;\r
5313     }\r
5314   } else {\r
5315     int err = CommDlgExtendedError();\r
5316     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5317     return FALSE;\r
5318   }\r
5319   return f;\r
5320 }\r
5321 \r
5322 \r
5323 \r
5324 VOID APIENTRY\r
5325 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5326 {\r
5327   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5328 \r
5329   /*\r
5330    * Get the first pop-up menu in the menu template. This is the\r
5331    * menu that TrackPopupMenu displays.\r
5332    */\r
5333   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5334 \r
5335   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5336 \r
5337   /*\r
5338    * TrackPopup uses screen coordinates, so convert the\r
5339    * coordinates of the mouse click to screen coordinates.\r
5340    */\r
5341   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5342 \r
5343   /* Draw and track the floating pop-up menu. */\r
5344   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5345                  pt.x, pt.y, 0, hwnd, NULL);\r
5346 \r
5347   /* Destroy the menu.*/\r
5348   DestroyMenu(hmenu);\r
5349 }\r
5350    \r
5351 typedef struct {\r
5352   HWND hDlg, hText;\r
5353   int sizeX, sizeY, newSizeX, newSizeY;\r
5354   HDWP hdwp;\r
5355 } ResizeEditPlusButtonsClosure;\r
5356 \r
5357 BOOL CALLBACK\r
5358 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5359 {\r
5360   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5361   RECT rect;\r
5362   POINT pt;\r
5363 \r
5364   if (hChild == cl->hText) return TRUE;\r
5365   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5366   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5367   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5368   ScreenToClient(cl->hDlg, &pt);\r
5369   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5370     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5371   return TRUE;\r
5372 }\r
5373 \r
5374 /* Resize a dialog that has a (rich) edit field filling most of\r
5375    the top, with a row of buttons below */\r
5376 VOID\r
5377 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5378 {\r
5379   RECT rectText;\r
5380   int newTextHeight, newTextWidth;\r
5381   ResizeEditPlusButtonsClosure cl;\r
5382   \r
5383   /*if (IsIconic(hDlg)) return;*/\r
5384   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5385   \r
5386   cl.hdwp = BeginDeferWindowPos(8);\r
5387 \r
5388   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5389   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5390   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5391   if (newTextHeight < 0) {\r
5392     newSizeY += -newTextHeight;\r
5393     newTextHeight = 0;\r
5394   }\r
5395   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5396     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5397 \r
5398   cl.hDlg = hDlg;\r
5399   cl.hText = hText;\r
5400   cl.sizeX = sizeX;\r
5401   cl.sizeY = sizeY;\r
5402   cl.newSizeX = newSizeX;\r
5403   cl.newSizeY = newSizeY;\r
5404   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5405 \r
5406   EndDeferWindowPos(cl.hdwp);\r
5407 }\r
5408 \r
5409 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5410 {\r
5411     RECT    rChild, rParent;\r
5412     int     wChild, hChild, wParent, hParent;\r
5413     int     wScreen, hScreen, xNew, yNew;\r
5414     HDC     hdc;\r
5415 \r
5416     /* Get the Height and Width of the child window */\r
5417     GetWindowRect (hwndChild, &rChild);\r
5418     wChild = rChild.right - rChild.left;\r
5419     hChild = rChild.bottom - rChild.top;\r
5420 \r
5421     /* Get the Height and Width of the parent window */\r
5422     GetWindowRect (hwndParent, &rParent);\r
5423     wParent = rParent.right - rParent.left;\r
5424     hParent = rParent.bottom - rParent.top;\r
5425 \r
5426     /* Get the display limits */\r
5427     hdc = GetDC (hwndChild);\r
5428     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5429     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5430     ReleaseDC(hwndChild, hdc);\r
5431 \r
5432     /* Calculate new X position, then adjust for screen */\r
5433     xNew = rParent.left + ((wParent - wChild) /2);\r
5434     if (xNew < 0) {\r
5435         xNew = 0;\r
5436     } else if ((xNew+wChild) > wScreen) {\r
5437         xNew = wScreen - wChild;\r
5438     }\r
5439 \r
5440     /* Calculate new Y position, then adjust for screen */\r
5441     if( mode == 0 ) {\r
5442         yNew = rParent.top  + ((hParent - hChild) /2);\r
5443     }\r
5444     else {\r
5445         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5446     }\r
5447 \r
5448     if (yNew < 0) {\r
5449         yNew = 0;\r
5450     } else if ((yNew+hChild) > hScreen) {\r
5451         yNew = hScreen - hChild;\r
5452     }\r
5453 \r
5454     /* Set it, and return */\r
5455     return SetWindowPos (hwndChild, NULL,\r
5456                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5457 }\r
5458 \r
5459 /* Center one window over another */\r
5460 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5461 {\r
5462     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5463 }\r
5464 \r
5465 /*---------------------------------------------------------------------------*\\r
5466  *\r
5467  * Startup Dialog functions\r
5468  *\r
5469 \*---------------------------------------------------------------------------*/\r
5470 void\r
5471 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5472 {\r
5473   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5474 \r
5475   while (*cd != NULL) {\r
5476     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5477     cd++;\r
5478   }\r
5479 }\r
5480 \r
5481 void\r
5482 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5483 {\r
5484   char buf1[MAX_ARG_LEN];\r
5485   int len;\r
5486 \r
5487   if (str[0] == '@') {\r
5488     FILE* f = fopen(str + 1, "r");\r
5489     if (f == NULL) {\r
5490       DisplayFatalError(str + 1, errno, 2);\r
5491       return;\r
5492     }\r
5493     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5494     fclose(f);\r
5495     buf1[len] = NULLCHAR;\r
5496     str = buf1;\r
5497   }\r
5498 \r
5499   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5500 \r
5501   for (;;) {\r
5502     char buf[MSG_SIZ];\r
5503     char *end = strchr(str, '\n');\r
5504     if (end == NULL) return;\r
5505     memcpy(buf, str, end - str);\r
5506     buf[end - str] = NULLCHAR;\r
5507     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5508     str = end + 1;\r
5509   }\r
5510 }\r
5511 \r
5512 void\r
5513 SetStartupDialogEnables(HWND hDlg)\r
5514 {\r
5515   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5516     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5517     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5518   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5519     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5520   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5521     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5522   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5523     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5524   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5525     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5526     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5527     IsDlgButtonChecked(hDlg, OPT_View));\r
5528 }\r
5529 \r
5530 char *\r
5531 QuoteForFilename(char *filename)\r
5532 {\r
5533   int dquote, space;\r
5534   dquote = strchr(filename, '"') != NULL;\r
5535   space = strchr(filename, ' ') != NULL;\r
5536   if (dquote || space) {\r
5537     if (dquote) {\r
5538       return "'";\r
5539     } else {\r
5540       return "\"";\r
5541     }\r
5542   } else {\r
5543     return "";\r
5544   }\r
5545 }\r
5546 \r
5547 VOID\r
5548 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5549 {\r
5550   char buf[MSG_SIZ];\r
5551   char *q;\r
5552 \r
5553   InitComboStringsFromOption(hwndCombo, nthnames);\r
5554   q = QuoteForFilename(nthcp);\r
5555   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5556   if (*nthdir != NULLCHAR) {\r
5557     q = QuoteForFilename(nthdir);\r
5558     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5559   }\r
5560   if (*nthcp == NULLCHAR) {\r
5561     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5562   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5563     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5564     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5565   }\r
5566 }\r
5567 \r
5568 LRESULT CALLBACK\r
5569 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5570 {\r
5571   char buf[MSG_SIZ];\r
5572   HANDLE hwndCombo;\r
5573   char *p;\r
5574 \r
5575   switch (message) {\r
5576   case WM_INITDIALOG:\r
5577     /* Center the dialog */\r
5578     CenterWindow (hDlg, GetDesktopWindow());\r
5579     /* Initialize the dialog items */\r
5580     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5581                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5582                   firstChessProgramNames);\r
5583     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5584                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5585                   secondChessProgramNames);\r
5586     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5587     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5588     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5589     if (*appData.icsHelper != NULLCHAR) {\r
5590       char *q = QuoteForFilename(appData.icsHelper);\r
5591       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5592     }\r
5593     if (*appData.icsHost == NULLCHAR) {\r
5594       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5595       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5596     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5597       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5598       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5599     }\r
5600 \r
5601     if (appData.icsActive) {\r
5602       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5603     }\r
5604     else if (appData.noChessProgram) {\r
5605       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5606     }\r
5607     else {\r
5608       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
5609     }\r
5610 \r
5611     SetStartupDialogEnables(hDlg);\r
5612     return TRUE;\r
5613 \r
5614   case WM_COMMAND:\r
5615     switch (LOWORD(wParam)) {\r
5616     case IDOK:\r
5617       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5618         strcpy(buf, "/fcp=");\r
5619         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5620         p = buf;\r
5621         ParseArgs(StringGet, &p);\r
5622         strcpy(buf, "/scp=");\r
5623         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5624         p = buf;\r
5625         ParseArgs(StringGet, &p);\r
5626         appData.noChessProgram = FALSE;\r
5627         appData.icsActive = FALSE;\r
5628       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5629         strcpy(buf, "/ics /icshost=");\r
5630         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5631         p = buf;\r
5632         ParseArgs(StringGet, &p);\r
5633         if (appData.zippyPlay) {\r
5634           strcpy(buf, "/fcp=");\r
5635           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5636           p = buf;\r
5637           ParseArgs(StringGet, &p);\r
5638         }\r
5639       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5640         appData.noChessProgram = TRUE;\r
5641         appData.icsActive = FALSE;\r
5642       } else {\r
5643         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5644                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5645         return TRUE;\r
5646       }\r
5647       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5648         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5649         p = buf;\r
5650         ParseArgs(StringGet, &p);\r
5651       }\r
5652       EndDialog(hDlg, TRUE);\r
5653       return TRUE;\r
5654 \r
5655     case IDCANCEL:\r
5656       ExitEvent(0);\r
5657       return TRUE;\r
5658 \r
5659     case IDM_HELPCONTENTS:\r
5660       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5661         MessageBox (GetFocus(),\r
5662                     "Unable to activate help",\r
5663                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5664       }\r
5665       break;\r
5666 \r
5667     default:\r
5668       SetStartupDialogEnables(hDlg);\r
5669       break;\r
5670     }\r
5671     break;\r
5672   }\r
5673   return FALSE;\r
5674 }\r
5675 \r
5676 /*---------------------------------------------------------------------------*\\r
5677  *\r
5678  * About box dialog functions\r
5679  *\r
5680 \*---------------------------------------------------------------------------*/\r
5681 \r
5682 /* Process messages for "About" dialog box */\r
5683 LRESULT CALLBACK\r
5684 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5685 {\r
5686   switch (message) {\r
5687   case WM_INITDIALOG: /* message: initialize dialog box */\r
5688     /* Center the dialog over the application window */\r
5689     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5690     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5691     JAWS_COPYRIGHT\r
5692     return (TRUE);\r
5693 \r
5694   case WM_COMMAND: /* message: received a command */\r
5695     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5696         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5697       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5698       return (TRUE);\r
5699     }\r
5700     break;\r
5701   }\r
5702   return (FALSE);\r
5703 }\r
5704 \r
5705 /*---------------------------------------------------------------------------*\\r
5706  *\r
5707  * Comment Dialog functions\r
5708  *\r
5709 \*---------------------------------------------------------------------------*/\r
5710 \r
5711 LRESULT CALLBACK\r
5712 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5713 {\r
5714   static HANDLE hwndText = NULL;\r
5715   int len, newSizeX, newSizeY, flags;\r
5716   static int sizeX, sizeY;\r
5717   char *str;\r
5718   RECT rect;\r
5719   MINMAXINFO *mmi;\r
5720 \r
5721   switch (message) {\r
5722   case WM_INITDIALOG: /* message: initialize dialog box */\r
5723     /* Initialize the dialog items */\r
5724     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5725     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5726     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5727     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5728     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5729     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5730     SetWindowText(hDlg, commentTitle);\r
5731     if (editComment) {\r
5732       SetFocus(hwndText);\r
5733     } else {\r
5734       SetFocus(GetDlgItem(hDlg, IDOK));\r
5735     }\r
5736     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5737                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5738                 MAKELPARAM(FALSE, 0));\r
5739     /* Size and position the dialog */\r
5740     if (!commentDialog) {\r
5741       commentDialog = hDlg;\r
5742       flags = SWP_NOZORDER;\r
5743       GetClientRect(hDlg, &rect);\r
5744       sizeX = rect.right;\r
5745       sizeY = rect.bottom;\r
5746       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
5747           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
5748         WINDOWPLACEMENT wp;\r
5749         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
5750         wp.length = sizeof(WINDOWPLACEMENT);\r
5751         wp.flags = 0;\r
5752         wp.showCmd = SW_SHOW;\r
5753         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5754         wp.rcNormalPosition.left = wpComment.x;\r
5755         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
5756         wp.rcNormalPosition.top = wpComment.y;\r
5757         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
5758         SetWindowPlacement(hDlg, &wp);\r
5759 \r
5760         GetClientRect(hDlg, &rect);\r
5761         newSizeX = rect.right;\r
5762         newSizeY = rect.bottom;\r
5763         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
5764                               newSizeX, newSizeY);\r
5765         sizeX = newSizeX;\r
5766         sizeY = newSizeY;\r
5767       }\r
5768     }\r
5769     return FALSE;\r
5770 \r
5771   case WM_COMMAND: /* message: received a command */\r
5772     switch (LOWORD(wParam)) {\r
5773     case IDOK:\r
5774       if (editComment) {\r
5775         char *p, *q;\r
5776         /* Read changed options from the dialog box */\r
5777         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5778         len = GetWindowTextLength(hwndText);\r
5779         str = (char *) malloc(len + 1);\r
5780         GetWindowText(hwndText, str, len + 1);\r
5781         p = q = str;\r
5782         while (*q) {\r
5783           if (*q == '\r')\r
5784             q++;\r
5785           else\r
5786             *p++ = *q++;\r
5787         }\r
5788         *p = NULLCHAR;\r
5789         ReplaceComment(commentIndex, str);\r
5790         free(str);\r
5791       }\r
5792       CommentPopDown();\r
5793       return TRUE;\r
5794 \r
5795     case IDCANCEL:\r
5796     case OPT_CancelComment:\r
5797       CommentPopDown();\r
5798       return TRUE;\r
5799 \r
5800     case OPT_ClearComment:\r
5801       SetDlgItemText(hDlg, OPT_CommentText, "");\r
5802       break;\r
5803 \r
5804     case OPT_EditComment:\r
5805       EditCommentEvent();\r
5806       return TRUE;\r
5807 \r
5808     default:\r
5809       break;\r
5810     }\r
5811     break;\r
5812 \r
5813   case WM_SIZE:\r
5814     newSizeX = LOWORD(lParam);\r
5815     newSizeY = HIWORD(lParam);\r
5816     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
5817     sizeX = newSizeX;\r
5818     sizeY = newSizeY;\r
5819     break;\r
5820 \r
5821   case WM_GETMINMAXINFO:\r
5822     /* Prevent resizing window too small */\r
5823     mmi = (MINMAXINFO *) lParam;\r
5824     mmi->ptMinTrackSize.x = 100;\r
5825     mmi->ptMinTrackSize.y = 100;\r
5826     break;\r
5827   }\r
5828   return FALSE;\r
5829 }\r
5830 \r
5831 VOID\r
5832 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
5833 {\r
5834   FARPROC lpProc;\r
5835   char *p, *q;\r
5836 \r
5837   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
5838 \r
5839   if (str == NULL) str = "";\r
5840   p = (char *) malloc(2 * strlen(str) + 2);\r
5841   q = p;\r
5842   while (*str) {\r
5843     if (*str == '\n') *q++ = '\r';\r
5844     *q++ = *str++;\r
5845   }\r
5846   *q = NULLCHAR;\r
5847   if (commentText != NULL) free(commentText);\r
5848 \r
5849   commentIndex = index;\r
5850   commentTitle = title;\r
5851   commentText = p;\r
5852   editComment = edit;\r
5853 \r
5854   if (commentDialog) {\r
5855     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
5856     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
5857   } else {\r
5858     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
5859     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
5860                  hwndMain, (DLGPROC)lpProc);\r
5861     FreeProcInstance(lpProc);\r
5862   }\r
5863   commentUp = TRUE;\r
5864 }\r
5865 \r
5866 \r
5867 /*---------------------------------------------------------------------------*\\r
5868  *\r
5869  * Type-in move dialog functions\r
5870  * \r
5871 \*---------------------------------------------------------------------------*/\r
5872 \r
5873 LRESULT CALLBACK\r
5874 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5875 {\r
5876   char move[MSG_SIZ];\r
5877   HWND hInput;\r
5878   ChessMove moveType;\r
5879   int fromX, fromY, toX, toY;\r
5880   char promoChar;\r
5881 \r
5882   switch (message) {\r
5883   case WM_INITDIALOG:\r
5884     move[0] = (char) lParam;\r
5885     move[1] = NULLCHAR;\r
5886     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5887     hInput = GetDlgItem(hDlg, OPT_Move);\r
5888     SetWindowText(hInput, move);\r
5889     SetFocus(hInput);\r
5890     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5891     return FALSE;\r
5892 \r
5893   case WM_COMMAND:\r
5894     switch (LOWORD(wParam)) {\r
5895     case IDOK:\r
5896       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
5897       { int n; Board board;\r
5898         // [HGM] FENedit\r
5899         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
5900                 EditPositionPasteFEN(move);\r
5901                 EndDialog(hDlg, TRUE);\r
5902                 return TRUE;\r
5903         }\r
5904         // [HGM] movenum: allow move number to be typed in any mode\r
5905         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
5906           ToNrEvent(2*n-1);\r
5907           EndDialog(hDlg, TRUE);\r
5908           return TRUE;\r
5909         }\r
5910       }\r
5911       if (gameMode != EditGame && currentMove != forwardMostMove && \r
5912         gameMode != Training) {\r
5913         DisplayMoveError("Displayed move is not current");\r
5914       } else {\r
5915 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
5916         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5917           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
5918         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
5919         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
5920           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
5921           if (gameMode != Training)\r
5922               forwardMostMove = currentMove;\r
5923           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
5924         } else {\r
5925           DisplayMoveError("Could not parse move");\r
5926         }\r
5927       }\r
5928       EndDialog(hDlg, TRUE);\r
5929       return TRUE;\r
5930     case IDCANCEL:\r
5931       EndDialog(hDlg, FALSE);\r
5932       return TRUE;\r
5933     default:\r
5934       break;\r
5935     }\r
5936     break;\r
5937   }\r
5938   return FALSE;\r
5939 }\r
5940 \r
5941 VOID\r
5942 PopUpMoveDialog(char firstchar)\r
5943 {\r
5944     FARPROC lpProc;\r
5945     \r
5946     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
5947         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
5948         gameMode == AnalyzeMode || gameMode == EditGame || \r
5949         gameMode == EditPosition || gameMode == IcsExamining ||\r
5950         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
5951         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
5952                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
5953                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
5954         gameMode == Training) {\r
5955       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
5956       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
5957         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
5958       FreeProcInstance(lpProc);\r
5959     }\r
5960 }\r
5961 \r
5962 /*---------------------------------------------------------------------------*\\r
5963  *\r
5964  * Type-in name dialog functions\r
5965  * \r
5966 \*---------------------------------------------------------------------------*/\r
5967 \r
5968 LRESULT CALLBACK\r
5969 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5970 {\r
5971   char move[MSG_SIZ];\r
5972   HWND hInput;\r
5973 \r
5974   switch (message) {\r
5975   case WM_INITDIALOG:\r
5976     move[0] = (char) lParam;\r
5977     move[1] = NULLCHAR;\r
5978     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
5979     hInput = GetDlgItem(hDlg, OPT_Name);\r
5980     SetWindowText(hInput, move);\r
5981     SetFocus(hInput);\r
5982     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
5983     return FALSE;\r
5984 \r
5985   case WM_COMMAND:\r
5986     switch (LOWORD(wParam)) {\r
5987     case IDOK:\r
5988       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
5989       appData.userName = strdup(move);\r
5990       SetUserLogo();\r
5991 \r
5992       EndDialog(hDlg, TRUE);\r
5993       return TRUE;\r
5994     case IDCANCEL:\r
5995       EndDialog(hDlg, FALSE);\r
5996       return TRUE;\r
5997     default:\r
5998       break;\r
5999     }\r
6000     break;\r
6001   }\r
6002   return FALSE;\r
6003 }\r
6004 \r
6005 VOID\r
6006 PopUpNameDialog(char firstchar)\r
6007 {\r
6008     FARPROC lpProc;\r
6009     \r
6010       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6011       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6012         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6013       FreeProcInstance(lpProc);\r
6014 }\r
6015 \r
6016 /*---------------------------------------------------------------------------*\\r
6017  *\r
6018  *  Error dialogs\r
6019  * \r
6020 \*---------------------------------------------------------------------------*/\r
6021 \r
6022 /* Nonmodal error box */\r
6023 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6024                              WPARAM wParam, LPARAM lParam);\r
6025 \r
6026 VOID\r
6027 ErrorPopUp(char *title, char *content)\r
6028 {\r
6029   FARPROC lpProc;\r
6030   char *p, *q;\r
6031   BOOLEAN modal = hwndMain == NULL;\r
6032 \r
6033   p = content;\r
6034   q = errorMessage;\r
6035   while (*p) {\r
6036     if (*p == '\n') {\r
6037       if (modal) {\r
6038         *q++ = ' ';\r
6039         p++;\r
6040       } else {\r
6041         *q++ = '\r';\r
6042         *q++ = *p++;\r
6043       }\r
6044     } else {\r
6045       *q++ = *p++;\r
6046     }\r
6047   }\r
6048   *q = NULLCHAR;\r
6049   strncpy(errorTitle, title, sizeof(errorTitle));\r
6050   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6051   \r
6052   if (modal) {\r
6053     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6054   } else {\r
6055     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6056     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6057                  hwndMain, (DLGPROC)lpProc);\r
6058     FreeProcInstance(lpProc);\r
6059   }\r
6060 }\r
6061 \r
6062 VOID\r
6063 ErrorPopDown()\r
6064 {\r
6065   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6066   if (errorDialog == NULL) return;\r
6067   DestroyWindow(errorDialog);\r
6068   errorDialog = NULL;\r
6069 }\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