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