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